Cours 2 Sockets

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 118

Systèmes distribués

Sockets TCP/UDP et leur


mise en œuvre en Java

Eric Cariou

Université de Pau et des Pays de l'Adour


Département Informatique

Eric.Cariou@univ-pau.fr
1
Plan
1. Sockets UDP
2. Les flux Java
3. Sockets TCP
4. Multicast IP
5. Concurrence
1. Les threads Java
2. Synchronisation en Java

2
Rappel sur les réseaux
 TCP ou UDP
 Communication entre systèmes aux extrémités
 Pas de visibilité des systèmes intermédiaires

Application Application
Communication d’extrémité
à extrémité
TCP/UDP TCP/UDP
IP IP IP
Liaison Liaison Liaison
Physique Physique Physique
3
Adressage
 Adressage pour communication entre applications
 Adresse « réseau » application = couple de 2 informations
 Adresse IP et numéro de port
 Couche réseau : adresse IP
 Ex : 192.129.12.34
 Couche transport : numéro de port TCP ou UDP
 Ce numéro est en entier d'une valeur quelconque
 Ports < 1024 : réservés pour les applications ou protocoles systèmes
 Exemple : 80 = HTTP, 21 = FTP, ...
 Sur un port : réception ou envoi de données
 Adresse notée : @IP:port ou nomMachine:port
 192.129.12.34:80 : accès au serveur Web tournant sur la machine
d'adresse IP 192.129.12.34
4
Sockets
 Socket : prise
 Associée, liée à un port
 C'est donc un point d'accès aux couches réseaux
 Services d'émission et de réception de données sur la
socket via le port
 En mode connecté (TCP)
 Connexion = tuyau entre 2 applications distantes
 Une socket est un des deux bouts du tuyau
 Chaque application a une socket locale pour gérer la
communication à distance
 Une socket peut-être liée
 Sur un port précis à la demande du programme
 Sur un port quelconque libre déterminé par le système
5
Sockets

 Une socket est


 Un point d'accès aux couches réseau TCP/UDP
 Liée localement à un port
 Adressage de l'application sur le réseau : son couple @IP:port
 Elle permet la communication avec un port distant sur une
6
machine distante : c'est-à-dire avec une application distante
Client/serveur avec sockets
 Il y a toujours différenciation entre une partie client
et une partie serveur
 Deux rôles distincts au niveau de la communication via
TCP/UDP
 Mais possibilité que les éléments communiquant jouent
un autre rôle ou les 2 en même temps
 Différenciation pour plusieurs raisons
 Identification : on doit connaître précisément la localisation
d'un des 2 éléments communiquants
 Le coté serveur communique via une socket liée à un port
précis : port d'écoute
 Dissymétrie de la communication/connexion
 Le client initie la connexion ou la communication
7
Sockets UDP

8
Sockets UDP : principe
 Mode datagramme
 Envois de paquets de données (datagrammes)
 Pas de connexion entre parties client et serveur
 Pas de fiabilité ou de gestion de la communication
 Un paquet peut ne pas arrivé (perdu par le réseau)
 Un paquet P2 envoyé après un paquet P1 peut arriver avant
ce paquet P1 (selon la gestion des routes dans le réseau)
 Principe de communication
 La partie serveur crée une socket et la lie à un port
UDP particulier
 La partie client crée une socket pour accéder à la
couche UDP et la lie sur un port quelconque
9
Sockets UDP : principe
 Principe de communication (suite)
 Le serveur se met en attente de réception de paquet sur
sa socket
 Le client envoie un paquet via sa socket en précisant
l'adresse du destinataire
 Couple @IP/port
 Destinataire = partie serveur
 @IP de la machine sur laquelle tourne la partie serveur et numéro
de port sur lequel est liée la socket de la partie serveur
 Il est reçu par le serveur (sauf pb réseau)
 Si le client envoie un paquet avant que le serveur ne soit
prêt à recevoir : le paquet est perdu

10
Sockets UDP en Java
 Java intègre nativement les fonctionnalités de
communication réseau au dessus de TCP-UDP/IP
 Package java.net
 Classes utilisées pour communication via UDP
 InetAddress : codage des adresses IP
 DatagramSocket : socket mode non connecté (UDP)
 DatagramPacket : paquet de données envoyé via une
socket sans connexion (UDP)

11
Sockets UDP en Java
 Classe InetAddress
 Constructeurs
 Pas de constructeurs, on passe par des méthodes statiques
pour créer un objet
 Méthodes
 public static InetAddress getByName(String host)
throws UnknownHostException
 Crée un objet InetAddress identifiant une machine dont le nom est
passé en paramètre
 L'exception est levée si le service de nom (DNS...) du système ne
trouve pas de machine du nom passé en paramètre sur le réseau
 Si précise une adresse IP sous forme de chaîne (''192.12.23.24'') au
lieu de son nom, le service de nom n'est pas utilisé
 Une autre méthode permet de préciser l'adresse IP sous forme d'un
tableau de 4 octets
12
Sockets UDP en Java
 Classe InetAddress
 Méthodes (suite)
 public static InetAddress getLocalHost()
throws UnknownHostException
 Retourne l'adresse IP de la machine sur laquelle tourne le
programme, c'est-à-dire l'adresse IP locale
 public String getHostName()
 Retourne le nom de la machine dont l'adresse est codée par l'objet
InetAddress

13
Sockets UDP en Java
 Classe DatagramPacket
 Structure des données en mode datagramme
 Constructeurs
 public DatagramPacket(byte[] buf, int length)
 Création d'un paquet pour recevoir des données (sous forme d'un
tableau d'octets)
 Les données reçues seront placées dans buf
 length précise la taille max de données à lire
 Ne pas préciser une taille plus grande que celle du tableau
 En général, length = taille de buf
 Variante du constructeur : avec un offset pour ne pas commencer
au début du tableau

14
Sockets UDP en Java
 Classe DatagramPacket
 Constructeurs (suite)
 public DatagramPacket(byte[] buf, int length,
InetAddress address, int port)
 Création d'un paquet pour envoyer des données (sous forme d'un
tableau d'octets)
 buf : contient les données à envoyer
 length : longueur des données à envoyer
 Ne pas préciser une taille supérieure à celle de buf
 address : adresse IP de la machine destinataire des données
 port : numéro de port distant (sur la machine destinataire) où
envoyer les données

15
Sockets UDP en Java
 Classe DatagramPacket
 Méthodes « get »
 InetAddress getAddress()
 Si paquet à envoyer : adresse de la machine destinataire
 Si paquet reçu : adresse de la machine qui a envoyé le paquet
 int getPort()
 Si paquet à envoyer : port destinataire sur la machine distante
 Si paquet reçu : port utilisé par le programme distant pour envoyer le
paquet
 byte[] getData
 Données contenues dans le paquet
 int getLength()
 Si paquet à envoyer : longueur des données à envoyer
 Si paquet reçu : longueur des données reçues
16
Sockets UDP en Java
 Classe DatagramPacket
 Méthodes « set »
 void setAddress(InetAdress adr)
 Positionne l'adresse IP de la machine destinataire du paquet
 void setPort(int port)
 Positionne le port destinataire du paquet pour la machine distante
 void setData(byte[] data)
 Positionne les données à envoyer
 int setLength(int length)
 Positionne la longueur des données à envoyer

17
Sockets UDP en Java
 Classe DatagramPacket, complément sur les
tailles des données envoyées
 Java n'impose aucune limite en taille pour les tableaux
d'octets circulant dans les paquets UDP, mais
 Pour tenir dans un seul datagramme IP, le datagramme UDP ne
doit pas contenir plus de 65467 octets de données
 Un datagramme UDP est rarement envoyé via plusieurs datagrammes IP
 Mais en pratique : il est conseillé de ne pas dépasser 8176 octets
 Car la plupart des systèmes limitent à 8 Ko la taille des datagrammes UDP
 Pour être certain de ne pas perdre de données : 512 octets max
 Si datagramme UDP trop grand : les données sont tronquées
 Si tableau d'octets en réception est plus petit que les
données envoyées
 Les données reçues sont généralement tronquées
18
Sockets UDP en Java
 Classe DatagramSocket
 Socket en mode datagramme
 Constructeurs
 public DatagramSocket() throws SocketException
 Crée une nouvelle socket en la liant à un port quelconque libre
 Exception levée en cas de problème (a priori il doit pas y en avoir)
 public DatagramSocket(int port)
throws SocketException
 Crée une nouvelle socket en la liant au port local précisé par le
paramètre port
 Exception levée en cas de problème : notamment quand le port est
déjà occupé

19
Sockets UDP en Java
 Classe DatagramSocket
 Méthodes d'émission/réception de paquet
 public void send(DatagramPacket p)
throws IOException
 Envoie le paquet passé en paramètre. Le destinataire est identifié par le
couple @IP/port précisé dans le paquet
 Exception levée en cas de problème d'entrée/sortie
 public void receive(DatagramPacket p)
throws IOException
 Reçoit un paquet de données
 Bloquant tant qu'un paquet n'est pas reçu
 Quand paquet arrive, les attributs de p sont modifiés
 Les données reçues sont copiées dans le tableau passé en paramètre
lors de la création de p et sa longueur est positionnée avec la taille des
données reçues
 Les attributs d'@IP et de port de p contiennent l'@IP et le port de la
socket distante qui a émis le paquet 20
Sockets UDP en Java
 Classe DatagramSocket
 Autres méthodes
 public void close()
 Ferme la socket et libère le port à laquelle elle était liée
 public int getLocalPort()
 Retourne le port local sur lequel est liée la socket
 Possibilité de créer un canal (mais toujours en mode
non connecté)
 Pour restreindre la communication avec un seul destinataire
distant
 Car par défaut peut recevoir sur la socket des paquets venant
de n'importe où

21
Sockets UDP en Java
 Classe DatagramSocket
 Réception de données : via méthode receive
 Méthode bloquante sans contrainte de temps : peut rester en
attente indéfiniment si aucun paquet n'est jamais reçu
 Possibilité de préciser un délai maximum d'attente
 public void setSoTimeout(int timeout)
throws SocketException
 L'appel de la méthode receive sera bloquante pendant au plus
timeout millisecondes
 Une méthode receive se terminera alors de 2 façons
 Elle retourne normalement si un paquet est reçu en moins du temps
positionné par l'appel de setSoTimeout
 L'exception SocketTimeoutException est levée pour indiquer que le
délai s'est écoulé avant qu'un paquet ne soit reçu
 SocketTimeoutException est une sous-classe de IOException
22
Sockets UDP Java – exemple coté client
 InetAddress adr;
DatagramPacket packet;
DatagramSocket socket;

// adr contient l'@IP de la partie serveur


adr = InetAddress.getByName("scinfr222");

// données à envoyer : chaîne de caractères


byte[] data = (new String("youpi")).getBytes();

// création du paquet avec les données et en précisant l'adresse du serveur


// (@IP et port sur lequel il écoute : 7777)
packet = new DatagramPacket(data, data.length, adr, 7777);

// création d'une socket, sans la lier à un port particulier


socket = new DatagramSocket();

// envoi du paquet via la socket


socket.send(packet);

23
Sockets UDP Java – exemple coté serveur
 DatagramSocket socket;
DatagramPacket packet;

// création d'une socket liée au port 7777


DatagramSocket socket = new DatagramSocket(7777);

// tableau de 15 octets qui contiendra les données reçues


byte[] data = new byte[15];

// création d'un paquet en utilisant le tableau d'octets


packet = new DatagramPacket(data, data.length);

// attente de la réception d'un paquet. Le paquet reçu est placé dans


// packet et ses données dans data.
socket.receive(packet);

// récupération et affichage des données (une chaîne de caractères)


String chaine = new String(packet.getData(), 0,
packet.getLength());
System.out.println(" recu : "+chaine);
24
Sockets UDP en Java – exemple suite
 La communication se fait souvent dans les 2 sens
 Le serveur doit donc connaître la localisation du client
 Elle est précisée dans le paquet qu'il reçoit du client
 Réponse au client, coté serveur
 System.out.println(" ca vient de :
"+packet.getAddress()+":"+ packet.getPort());

// on met une nouvelle donnée dans le paquet


// (qui contient donc le couple @IP/port de la socket coté client)
packet.setData((new String("bien recu")).getBytes());

// on envoie le paquet au client


socket.send(packet);

25
Sockets UDP en Java – exemple suite
 Réception réponse du serveur, coté client

// attente paquet envoyé sur la socket du client


socket.receive(packet);

// récupération et affichage de la donnée contenue dans le paquet


String chaine = new String(packet.getData(), 0,
packet.getLength());
System.out.println(" recu du serveur : "+chaine);

26
Critique sockets UDP
 Avantages
 Simple à programmer (et à appréhender)
 Inconvénients
 Pas fiable
 Ne permet d'envoyer que des tableaux de byte

27
Structure des données échangées
 Format des données à transmettre
 Très limité a priori : tableaux de byte
 Et attention à la taille réservée : si le récepteur réserve un
tableau trop petit par rapport à celui envoyé, une partie des
données est perdue
 Doit donc pouvoir convertir
 Un objet quelconque en byte[] pour l'envoyer
 Un byte[] en un objet d'un certain type après réception
 Deux solutions
 Créer les méthodes qui font cela : lourd et dommage de faire
des tâches de si « bas-niveau » avec un langage évolué
comme Java
 Utiliser les flux Java pour conversion automatique (voir suite)
28
Flux Java

29
Flux Java
 En Java, toutes les entrées/sorties sont gérées via
des flux
 Entrées/sorties standards (clavier/console)
 Fichiers
 Sockets
 ...
 Flux : tuyaux dans lesquels on envoie ou lit des
séries de données
 Information de base qui transite dans un flux : l'octet

30
Flux Java standards
 Flux d'entrées/sortie standards
 System.out
 Sortie standard, flux de type PrintStream
 System.out.println(''nombre = ''+nb);
 System.err
 Sortie d'erreur strandard, flux de type PrintStream
 System.in
 Entrée standard, flux de type InputStream
 while ((c = (char)System.in.read()) != 'z')
System.out.print(c);

31
Hiérarchie de flux Java
 Java définit une hiérarchie de flux composée de plusieurs
dizaines de classes (de types de flux différents)
 Package java.io
 Deux classifications transverses
 Flux est soit d'entrée, soit de sortie
 Entrée : le programme lit des informations à partir du flux
 Sortie : le programme écrit des informations dans le flux
 Nature de l'information transitant sur le flux
 Binaire : octet par octet
 Caractère : 2 octets par 2 octets
 Codage unicode sur 16 bits

32
Hiérarchie de flux Java
 Hiérarchie principale
 Flux de base
 Flux avec tampon
 Flux d'accès aux fichiers
 Flux de filtrage
 Flux d'impression
 Flux enchaînés par des « pipes »
 Flux de concaténation de plusieurs flux en un seul
 Flux de conversion flux caractère/flux binaire
 Flux de lecture/écriture de différents types
 int, char ... ou bien encore un objet quelconque (Object)
 Données codées indépendamment de la plateforme/système
33
Hiérarchie de flux Java
 Flux binaire, entrée

34
Hiérarchie de flux Java
 Flux binaire, sortie

35
Hiérarchie de flux Java
 Flux caractère, entrée

36
Hiérarchie de flux Java
 Flux caractère, sortie

37
Hiérarchie de flux Java
 Autres types de flux
 Package java.util.zip
 Compression données : GZIPInputStream, ZipInputStream ...
 Vérification intégrité données (CRC) : CheckedInputStream ...
 Package javax.crypto
 Cryptage des données : CipherInputStream ...
 Et d'autres ...
 Les flux peuvent être dépendants les uns des autres
 Un flux est créé à partir d'un autre (par « wrapping ») : il traite
les mêmes données mais avec un traitement supplémentaire
 Codage des données dans un autre type
 Filtrage des données, mise en tapon ...
 Un flux est chaîné à un autre par un pipe
38
Méthodes des classes Stream
 Méthodes générales d'accès aux données du flux
 Flux en entrée (InputStream)
 int read()
 Lecture d'un octet (sous forme de int) dans le flux
 int read(byte[] tab)
 Lit une suite d'octets en les plaçant dans tab
 Lit au plus la longueur de tab
 Retourne le nombre d'octets lu
 Autres méthodes pour se placer à un endroit donné du flux ...
 int available()
 Retourne le nombre d'octets disponibles en lecture dans le flux
 void close()
 Ferme le flux
39
Méthodes des classes Stream
 Méthodes générales d'accès aux données du flux
 Flux en sortie (OutputStream)
 void write(int b)
 Écrit un octet (via un int) dans le flux
 void write(byte[])
 Écrit le contenu d'un tableau d'octets dans le flux
 void flush()
 Force l'écriture dans le flux de toutes les données à écrire
 Vide le tampon associé au flux en écrivant son contenu
 void close()
 Ferme le flux
 Flux en entrées ou sorties
 Méthodes générales : accès niveau octet
40
Méthodes des classes Stream
 Classes de flux spécialisées
 Offrent des méthodes d'accès plus évoluées que niveau octet
 Deux types de flux intéressants de ce point de vue
 Data[Input/Output]Stream
 Lecture/écriture de types primitifs Java
 int, char, boolean, double, long, byte, float, short
 Exemple pour double
 DataOutputStream : void writeDouble(double b)
 DataInputStream : double readDouble()
 Object[Input/Output]Stream
 Lecture/écriture d'objets de toute nature
 Très puissant et confortable
 ObjectOutputStream : void writeObject(Object o)
 ObjectInputStream : Object readObject()
41
Méthodes des classes Stream
 Data[Input/Output]Stream (suite)
 Exemple : écriture d'un objet de la classe Personne (classe
programmée n'appartenant pas à la hiérarchie Java)
 Personne pers = new Personne (''toto'', 24);
ObjectOutputStream output = .... ;
output.writeObject(pers);
 Pour pouvoir envoyer un objet dans un flux
 Sa classe doit implémenter l'interface
java.io.Serializable
 Interface vide qui sert juste à préciser que les objets peuvent
être sérialisés
 C'est-à-dire peuvent être transformés en série de byte et sont donc
transmissibles via des flux

42
Méthodes des classes Stream
 Exceptions niveau flux
 La plupart des méthodes d'accès aux flux peuvent lever
l'exception java.io.IOException
 Problème quelconque d'entrée/sortie ...
 Constructeurs
 Les flux évolués peuvent être construits à partir d'autres
flux existants
 Exemple : créer un ObjectOutputStream à partir d'un
FileOutputStream associé au fichier test.bin
 FileOutputStream fileOut=
new FileOutputStream(''test.bin'');
ObjectOutputStream objOut =
new ObjectOutputStream(fileOut);
// peut maintenant enregistrer tout objet dans test.bin via objOut
43
Exemple utilisation de flux Java
 Exemple concret d'utilisation de flux
 Écriture d'entiers dans un fichier
// ouverture d'un flux en sortie sur le fichier entiers.bin
FileOutputStream ficOut =
new FileOutputStream("entiers.bin");

// ouverture d'un flux de données en sortie à partir de ce flux


DataOutputStream dataOut =
new DataOutputStream(ficOut);

// écriture des entiers de 10 à 15 dans le fichier


for(int i=10;i<16;i++)
dataOut.writeInt(i);

// fermeture des flux


dataOut.close();
ficOut.close();
44
Exemple utilisation de flux Java
 Exemple concret d'utilisation de flux
 Lecture d'entiers à partir d'un fichier
// ouverture d'un flux en entrée sur le fichier entiers.bin
FileInputStream ficIn =
new FileInputStream("entiers.bin");

// ouverture d'un flux de données en entrée à partir de ce flux


DataInputStream dataIn = new DataInputStream(ficIn);

// tant que des données sont disponibles, on lit des entiers


while(dataIn.available() > 0)
System.out.println(dataIn.readInt());

// fermeture des flux


dataIn.close();
ficIn.close();

45
Conversion Object <-> byte[]
 Pour émettre et recevoir n'importe quel objet via
des sockets UDP
 En écriture : conversion de Object en byte[]
ByteArrayOutputStream byteStream =
new ByteArrayOutputStream();
ObjectOutputStream objectStream =
new ObjectOutputStream(byteStream);
objectStream.writeObject(object);
byte[] byteArray = byteStream.toByteArray();
 En lecture : conversion de byte[] en Object
ByteArrayInputStream byteStream =
new ByteArrayInputStream(byteArray);
ObjectInputStream objectStream =
new ObjectInputStream(byteStream);
object = objectStream.readObject();
46
Sockets TCP

47
Sockets TCP : principe
 Fonctionnement en mode connecté
 Données envoyées dans un « tuyau » et non pas par paquet
 Flux de données
 Correspond aux flux Java dans la mise en oeuvre Java des sockets TCP
 Fiable : la couche TCP assure que
 Les données envoyées sont toutes reçues par la machine destinataire
 Les données sont reçues dans l'ordre où elles ont été envoyées

48
Sockets TCP : principe
 Principe de communication
 Le serveur lie une socket d'écoute sur un certain port bien
précis et appelle un service d'attente de connexion de la
part d'un client
 Le client appelle un service pour ouvrir une connexion
avec le serveur
 Il récupère une socket (associée à un port quelconque par le
système)
 Du coté du serveur, le service d'attente de connexion retourne
une socket de service (associée à un port quelconque)
 C'est la socket qui permet de dialoguer avec ce client
 Comme avec sockets UDP : le client et le serveur
communiquent en envoyant et recevant des données via leur
socket
49
Sockets TCP en Java
 Classes du package java.net utilisées pour
communication via TCP
 InetAddress : codage des adresses IP
 Même classe que celle décrite dans la partie UDP et usage
identique
 Socket : socket mode connecté
 ServerSocket : socket d'attente de connexion du coté
server

50
Sockets TCP en Java
 Classe Socket
 Socket mode connecté
 Constructeurs
 public Socket(InetAddress address, int port)
throws IOException
 Crée une socket locale et la connecte à un port distant d'une machine
distante identifié par le couple address/port
 public Socket(String address, int port)
throws IOException,UnknownHostException
 Idem mais avec nom de la machine au lieu de son adresse IP codée
 Lève l'exception UnknownHostException si le service de nom ne
parvient pas à identifier la machine
 Variante de ces 2 constructeurs pour préciser en plus un port local
sur lequel sera liée la socket créée
51
Sockets TCP en Java
 Classe Socket
 Méthodes d'émission/réception de données
 Contrairement aux sockets UDP, les sockets TCP n'offre pas
directement de services pour émettre/recevoir des données
 On récupère les flux d'entrée/sorties associés à la socket
 OutputStream getOutputStream()
 Retourne le flux de sortie permettant d'envoyer des données via la
socket
 InputStream getInputStream()
 Retourne le flux d'entrée permettant de recevoir des données via la
socket
 Fermeture d'une socket
 public close()
 Ferme la socket et rompt la connexion avec la machine distante

52
Sockets TCP en Java
 Classe Socket
 Méthodes « get »
 int getPort()
 Renvoie le port distant avec lequel est connecté la socket
 InetAddress getAddress()
 Renvoie l'adresse IP de la machine distante
 int getLocalPort()
 Renvoie le port local sur lequel est liée la socket
 public void setSoTimeout(int timeout)
throws SocketException
 Positionne l'attente maximale en réception de données sur le
flux d'entrée de la socket
 Si temps dépassé lors d'une lecture : exception
SocketTimeoutException est levée
 Par défaut : temps infini en lecture sur le flux
53
Sockets TCP en Java
 Classe ServerSocket
 Socket d'attente de connexion, coté serveur uniquement
 Constructeurs
 public ServerSocket(int port) throws IOException
 Crée une socket d'écoute (d'attente de connexion de la part de clients)
 La socket est liée au port dont le numéro est passé en paramètre
 L'exception est levée notamment si ce port est déjà lié à une socket

 Méthodes
 Socket accept() throws IOException
 Attente de connexion d'un client distant
 Quand connexion est faite, retourne une socket permettant de
communiquer avec le client : socket de service
 void setSoTimeout(int timeout) throws SocketException
 Positionne le temps maximum d'attente de connexion sur un accept
 Si temps écoulé, l'accept lève l'exception SocketTimeoutException
 Par défaut, attente infinie sur l'accept 54
Sockets TCP Java – exemple coté client
 Même exemple qu'avec UDP
 Connexion d'un client à un serveur
 Envoi d'une chaîne par le client et réponse sous forme
d'une chaîne par le serveur
 Coté client

// adresse IP du serveur
InetAddress adr = InetAddress.getByName("scinfr222");

// ouverture de connexion avec le serveur sur le port 7777


Socket socket = new Socket(adr, 7777);

55
Sockets TCP Java – exemple coté client
 Coté client (suite)

// construction de flux objets à partir des flux de la socket


ObjectOutputStream output =
new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream input =
new ObjectInputStream(socket.getInputStream());

// écriture d'une chaîne dans le flux de sortie : c'est-à-dire envoi de


// données au serveur
output.writeObject(new String("youpi"));

// attente de réception de données venant du serveur (avec le readObject)


// on sait qu'on attend une chaîne, on peut donc faire un cast directement
String chaine = (String)input.readObject();
System.out.println(" recu du serveur : "+chaine);

56
Sockets TCP Java – exemple coté serveur
 // serveur positionne sa socket d'écoute sur le port local 7777
ServerSocket serverSocket = new ServerSocket(7777);

// se met en attente de connexion de la part d'un client distant


Socket socket = serverSocket.accept();

// connexion acceptée : récupère les flux objets pour communiquer


// avec le client qui vient de se connecter
ObjectOutputStream output =
new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream input =
new ObjectInputStream(socket.getInputStream());

// attente les données venant du client


String chaine = (String)input.readObject();
System.out.println(" recu : "+chaine);

57
Sockets TCP Java – exemple coté serveur
 Coté serveur (suite)
// affiche les coordonnées du client qui vient de se connecter
System.out.println(" ca vient de : "
+socket.getInetAddress()+":"+socket.getPort());

// envoi d'une réponse au client


output.writeObject(new String("bien recu"));

 Quand manipule des flux d'objets


 Souvent utile de vérifier le type de l'objet reçu
 Utilise instanceof
 Exemple
 String chaine; Personne pers;
Object obj = input.readObject();
if (obj instanceof String) chaine = (String)obj;
if (obj instanceof Personne) pers = (Personne)obj;
58
Sockets TCP
 Critique sockets TCP
 Avantages
 Niveau d'abstraction plus élevé qu'avec UDP
 Mode connecté avec phase de connexion explicite
 Flux d'entrée/sortie
 Fiable
 Inconvénients
 Plus difficile de gérer plusieurs clients en même temps
 Nécessite du parallélisme avec des threads (voir suite cours)
 Mais oblige une bonne structuration coté serveur

59
Sockets UDP ou TCP ?
 Choix entre UDP et TCP
 A priori simple
 TCP est fiable et mieux structuré
 Mais intérêt tout de même pour UDP dans certains cas
 Si la fiabilité n'est pas essentielle
 Si la connexion entre les 2 applications n'est pas utile
 Exemple
 Un thermomètre envoie toutes les 5 secondes la température de
l'air ambiant à un afficheur distant
 Pas grave de perdre une mesure de temps en temps
 Pas grave d'envoyer les mesures même si l'afficheur est absent

60
Sockets UDP ou TCP ?
 Exemple de protocole utilisant UDP : NFS
 Network File System (NFS)
 Accès à un système de fichiers distant
 A priori TCP mieux adapté car besoin de fiabilité lors des
transferts des fichiers, mais
 NFS est généralement utilisé au sein d'un réseau local
 Peu de pertes de paquets
 UDP est plus basique et donc plus rapide
 TCP gère un protocole assurant la fiabilité impliquant de nombreux
échanges supplémentaires entre les applications (envoi d'acquittement...)
 Peu de perte de paquet en UDP en local : peu directement gérer la fiabilité
au niveau NFS ou applicatif et c'est moins couteux en temps
 Dans ce contexte, il n'est pas pénalisant d'utiliser UDP au lieu de
TCP pour NFS
 NFS fonctionne sur ces 2 couches
61
Multicast IP

62
Multicast
 On a vu comment faire communiquer des
applications 1 à 1 via des sockets UDP ou TCP
 UDP offre un autre mode de communication : multicast
 Plusieurs récepteurs pour une seule émission d'un paquet
 Broadcast, multicast
 Broadcast (diffusion) : envoi de données à tous les éléments
d'un réseau
 Multicast : envoie de données à un sous-groupe de tous les
éléments d'un réseau
 Multicast IP
 Envoi d'un datagramme sur une adresse IP particulière
 Plusieurs éléments lisent à cette adresse IP
63
Multicast
 Adresse IP multicast
 Classe d'adresse IP entre 224.0.0.0 et 239.255.255.255
 Classe D
 Adresses entre 225.0.0.0 et 238.255.255.255 sont utilisables par
un programme quelconque
 Les autres sont réservées
 Une adresse IP multicast n'identifie pas une machine sur
un réseau mais un groupe multicast
 Socket UDP multicast
 Avant envoi de paquet : on doit rejoindre un groupe
 Identifié par un couple : @IP multicast/numéro port
 Un paquet envoyé par un membre du groupe est reçu par
tous les membres de ce groupe
64
Multicast
 Utilités du multicast UDP/IP
 Evite d'avoir à créer X connexions et/ou d'envoyer X fois
la même donnée à X machines différentes
 En pratique
 Utilisé pour diffuser des informations
 Diffusion de flux vidéos à plusieurs récepteurs
 Chaine de télévision, diffusion d'une conférence
 Le même flux est envoyé à tous au même moment
 Pour récupérer des informations sur le réseau
 224.0.0.12 : pour localiser un serveur DHCP
 Limites
 Non fiable et non connecté comme UDP

65
Multicast UDP en Java
 Classe java.net.MulticastSocket
 Spécialisation de DatagramSocket
 Constructeurs : identiques à ceux de DatagramSocket
 public DatagramSocket() throws SocketException
 Crée une nouvelle socket en la liant à un port quelconque libre
 Exception levée en cas de problème (a priori il doit pas y en avoir)
 public DatagramSocket(int port)
throws SocketException
 Crée une nouvelle socket en la liant au port précisé par le
paramètre port : c'est le port qui identifie le groupe de multicast
 Exception levée en cas de problème

66
Multicast UDP en Java
 Classe java.net.MulticastSocket (suite)
 Gestion des groupes
 public void joinGroup(InetAddress mcastaddr)
throws IOException
 Rejoint le groupe dont l'adresse IP multicast est passée en
paramètre
 L'exception est levée en cas de problèmes, notamment si l'adresse
IP n'est pas une adresse IP multicast valide
 public void leaveGroup(InetAddress mcastaddr)
throws IOException
 Quitte un groupe de multicast
 L'exception est levée si l'adresse IP n'est pas une adresse IP
multicast valide
 Pas d'exception levée ou de problème quand on quitte un groupe
auquel on appartient pas
67
Multicast UDP en Java
 Classe java.net.MulticastSocket (suite)
 Emission/réception de données
 On utilise les services send() et receive() avec des
paquets de type DatagramPacket tout comme avec une
socket UDP standard
 Exemple, exécution dans l'ordre :
 Connexion à un groupe
 Envoi d'un paquet
 Réception d'un paquet
 Quitte le groupe

68
Multicast UDP en Java
 Exemple de communication via socket multicast UDP
// adresse IP multicast du groupe
InetAddress group = InetAddress.getByName("228.5.6.7");

// socket UDP multicast pour communiquer avec groupe 228.5.6.7:4000


MulticastSocket socket = new MulticastSocket(4000);

// données à envoyer
byte[] data = (new String(''youpi'')).getBytes();

// paquet à envoyer (en précisant le couple @IP/port du groupe)


DatagramPacket packet =
new DatagramPacket(data, data.length, group, 4000);

// on joint le groupe
socket.joinGroup(group);

69
Multicast UDP en Java
 Exemple (suite)
// on envoie le paquet
socket.send(packet);

// attend un paquet en réponse


socket.receive(packet);

// traite le résultat
...
// quitte le groupe
socket.leaveGroup(group);
 Notes
 Il est possible que le receive récupère le paquet que le send vient
juste d'envoyer
 Besoin d'un autre receive pour réponse venant d'un autre élément

70
Concurrence dans une application

Threads Java

71
Concurrence
 Par principe, les éléments distants
communiquants sont actifs en parallèle
 Plusieurs processus concurrents
 Avec processus en pause lors d'attente de messages
 Exemple de flux d'exécution pour notre exemple
de client/serveur précédent

temps

72
Sockets TCP – gestion plusieurs clients
 Particularité coté serveur en TCP
 Une socket d'écoute sert à attendre les connexions des
clients
 A la connexion d'un client, une socket de service est
initialisée pour communiquer avec ce client
 Communication avec plusieurs clients pour le serveur
 Envoi de données à un client
 UDP : on précise l'adresse du client dans le paquet à envoyer
 TCP : utilise la socket correspondant au client
 Réception de données venant d'un client quelconque
 UDP : se met en attente d'un paquet et regarde de qui il vient
 TCP : doit se mettre en attente de données sur toutes les
sockets actives
73
Sockets TCP – gestion plusieurs clients
 Communication avec plusieurs clients (suite)
 Contrainte
 Lecture sur une socket : opération bloquante
 Tant que des données ne sont pas reçues
 Attente de connexion : opération bloquante
 Jusqu'à la prochaine connexion d'un client distant
 Avec un seul flot d'exécution (processus/thread)
 Si ne sait pas quel est l'ordonnancement des arrivées des données
des clients ou de leur connexion au serveur
 Impossible à gérer
 Donc nécessité de plusieurs processus ou threads
 Un processus en attente de connexion sur le port d'écoute
 Nouvelle connexion : un nouveau processus est créé pour gérer la
communication avec le nouveau client
74
Sockets TCP – gestion plusieurs clients
 Boucle de fonctionnement général d'un serveur
pour gérer plusieurs clients
 while(true)
socketClient = acceptConnection()
newThread(socketClient)
 Exemple
avec 2
clients ->

75
Gestion plusieurs clients
 Java offre nativement un mécanisme permettant
de gérer des flux d'exécution parallèle
 Les threads
 Rappel différence processus/thread
 Le processus est créé comme une copie d'un processus
existant
 Deux processus distincts avec leur mémoire propre
 Le thread s'exécute au sein d'un processus existant
 Nouveau flux d'exécution interne
 Partage des données du processus

76
Threads en Java
 Pour créer et lancer un nouveau thread, 2 modes
 Etendre la classe java.lang.Thread
 Redéfinir la méthode public void run()
 Qui contient la séquence de code qu'exécutera le thread
 Pour lancer le thread
 Instancier normalement la classe définie
 Appeler ensuiter la méthode start() sur l'objet créé
 Implémenter l'interface java.lang.Runnable
 Définir la méthode public void run() de cette interface
 Qui contient la séquence de code qu'exécutera la thread
 Pour lancer le thread
 Instancier normalement la classe définie
 Créer une instance de la classe Thread en passant cet objet en
paramètre
 Lancer la méthode start() du thread instancié 77
Thread Java – exemple
 Classe CalculFactoriel calcule un factoriel et affiche
le résultat à l'écran
 Via un thread à part
public class CalculFactoriel extends Thread {

protected int nb;

public void run(){


int res = 1;
for (int i=1; i<=nb; i++)
res = res * i;
System.out.println(''factoriel de ''+nb+''=''+res);
}

public CalculFactoriel(int nb) {


this.nb = nb; }
}
78
Thread Java – exemple
 Lancement du calcul des factoriels de 1 à 10 en
parallèle
 ...
CalculFactoriel cf;
for (int i=10; i >= 1; i--) {
cf = new CalculFactoriel(i);
cf.start();
}
...
 Deux phases pour lancer un calcul
 On instantie normalement la classe CalculFactoriel
 On appelle la méthode start() sur l'objet créé
 La séquence d'instructions de la méthode run() de la classe
CalculFactoriel est exécutée via un nouveau thread créé
79
Thread Java – variante exemple
 Même exemple mais sans spécialiser la classe Thread
 Implémentation de l'interface Runnable
public class CalculFactoriel implements Runnable {

protected int nb;

public void run(){


int res = 1;
for (int i=1; i<=nb; i++)
res = res * i;
System.out.println(''factoriel de ''+nb+''=''+res);
}

public CalculFactoriel(int nb) {


this.nb = nb; }
}

80
Thread Java – variante exemple
 Lancement des threads
 ...
CalculFactoriel cf;
for (int i=10; i >= 1; i--) {
cf = new CalculFactoriel(i);
(new Thread(cf)).start();
}
...
 On lance un Thread générique qui exécutera la
méthode run() de l'objet de type Runnable passé en
paramètre du constructeur

81
Thread Java – création
 2 méthodes pour créer et exécuter un thread
 Laquelle choisir ?
 A priori peu de différence
 Sauf dans le cas où la classe doit hériter d'une autre
classe
 Cas typique d'une applet
 public MaClasse extends Applet implements Runnable
 Doit alors forcément utiliser l'interface Runnable

82
Thread Java – résultat exemple
 (un) résultat de l'exécution du programme
 factoriel de 10=3628800
factoriel de 9=362880
factoriel de 8=40320
factoriel de 7=5040
factoriel de 6=720
factoriel de 5=120
factoriel de 4=24
factoriel de 3=6
factoriel de 2=2
factoriel de 1=1
 Les résultats des calculs sont affichés dans l'ordre de
leur lancement
 Pourtant les calculs de petites valeurs sont normalement plus
courts car moins de passages dans la boucle ...
83
Ordonnancement des threads
 Ordonnancement des processus/threads
 Sur une machine, nombre de flots d'exécution en réel
parallélisme = nombre de processeurs
 Les processus/threads doivent partager les supports
d'exécution pour s'exécuter
 Pour simuler un parallélisme d'exécution avec un seul
processeur
 Un processus/thread n'est pas exécuté du début à la fin en une
seule étape
 Un processus/thread exécute une partie de ses instructions
pendant un temps donné avant de passer la main à un autre
processus
 Plus tard, il retrouvera la main et continuera son exécution
84
Ordonnancement des threads
 Dépendance thread/processus
 Un thread est créé par et dans un processus
 Selon le système d'exploitation, l'ordonnancement se fait
 Uniquement au niveau processus
 Le système s'occupe de gérer uniquement le parallélisme des processus
 Un processus gère en interne l'ordonnancement de ses propres threads
 Au niveau de tous les thread et processus
 Les threads des processus et les processus sont ordonnancés par le
système
 Approche mixte
 L'ordonnancement se fait au niveau processus mais certains threads
particuliers peuvent être ordonnancés par le système au même niveau
que les processus

85
Ordonnancement des threads
 Deux types d'ordonnancement par le système (ou
par le processus pour ordonnancer ses threads)
 Préemptif
 Le système interrompt l'exécution des processus/threads pour
partager l'accès au processeur
 Le système décide quel est le prochain processus/thread qui
continuera son exécution
 Coopératif
 Un processus/thread ne libère le processeur que
 Quand il est bloqué momentanément (entrée/sortie ...)
 De sa propre initiative
 Le système décide alors quel est le prochain processus/thread qui
continuera son exécution

86
Ordonnancement des threads Java
 Ordonnancement des threads en Java
 Exécution d'une machine virtuelle Java
 Via un processus du système d'exploitation
 Qui exécute plusieurs threads Java
 Le thread principal
 Correspondant au static void main(String argv[])
 Les threads créés par le programme
 Les threads gérant l'interface graphique
 Garbage collector ...
 Particularité de Java
 Langage multi-plateformes (windows, linux, solaris, ...)
 L'ordonnancement des processus/threads dépend du
système d'exploitation
87
Ordonnancement des threads Java
 Principe fondamental
 On ne doit pas se baser sur un modèle
d'ordonnancement particulier pour développer une
application multi-threadée en Java
 Par principe, on considèrera le modèle le plus
contraignant
 Généralement c'est l'ordonnancement coopératif des
threads Java
 Si on veut un parallélisme « correct », tout thread doit
relacher la main de temps en temps
 Sans oublier qu'en cas de synchronisation/communication
obligatoire entre threads, il faut que tout thread ait la main
régulièrement
88
Ordonnancement des threads
 Les threads peuvent avoir des priorités différentes
 Un thread plus prioritaire a la main en priorité
 Si un thread de plus haute priorité que le thread courant actif veut la
main, il la récupère alors de suite
 Toujours via un ordonnancement préemptif
 Accès aux priorités, méthodes de la classe Thread
 public int getPriority() : retourne le niveau de priorité du
thread
 public void setPriority(int priority) : change le
niveau de priorité du thread
 Trois constantes de la classe Thread pour définir les priorités
 MAX_PRIORITY : niveau de priorité maximal possible (10)
 MIN_PRIORITY : niveau de priorité minimal possible (1)
 NORM_PRIORITY : niveau de priorité par défaut (5)
89
Ordonnancement des threads Java
 Retour sur l'exemple du calcul de factoriel
 Une fois que la méthode run() d'un thread est commencée,
on doit donc supposer que ce thread garde au pire le
processeur jusqu'à la fin de sa méthode run()
 Pour avoir un meilleur parallélisme, il faut qu'un thread passe
la main à un autre thread de temps en temps
 Dans la classe java.lang.Thread
 public static void yield()
 Le thread s'interrompt et passe la main à un autre thread
 Modification de l'exemple
 Ajout d'un yield() après chaque calcul dans run()
for (int i=1; i<=nb; i++) {
res = res * i;
Thread.yield(); }
90
Thread Java – nouveau résultat exemple
 (un) résultat d'exécution de l'exemple après la modification
 factoriel de 1=1
factoriel de 2=2
factoriel de 3=6
factoriel de 4=24
factoriel de 5=120
factoriel de 6=720
factoriel de 7=5040
factoriel de 8=40320
factoriel de 9=362880
factoriel de 10=3628800
 Bien que lancés en dernier, les calculs les plus courts se
terminent en premier
 Ordonnancement plus « naturel » que le précédent
 Correspond à ce que l'on aurait avec un parrallélisme physique complet
 Mais aurait pu avoir un ordre moins « parfait »
 1, 2, 4, 3, 5, 7, 6, 8, 9, 10 par exemple 91
Ordonnancement des threads
 Un thread passe la main à un autre dès qu'il est bloqué ou
en attente, c'est-à-dire dans les cas suivants
 Il est bloqué en attente sur une entrée/sortie (flux)
 Il est bloqué sur l'accès à un objet synchronisé
 Il se met en attente avec un wait()
 Il fait une pause pendant une certaine durée avec un sleep()
 Il a executé un yield() pour céder explicitement la main
 Il se met en attente de la terminaison d'un autre thread avec un join()
 Il se termine
 Un thread de plus haute priorité demande la main
 Une application Java se termine quand
 Le main() et tous les run() de tous les threads créés sont
terminés
92
Interactions entre threads
 Les threads sont des objets comme les autres
 Ils possèdent des références sur d'autres objets
 Un thread peut appeler des méthodes sur ces objets
 On peut appeler des méthodes sur le thread
 Communication/interaction possible via ces objets ou les
méthodes du thread
 Avec mécanisme possible d'accès en exclusion mutuelle
 Relations entre les cycles de vie des threads
 Un thread peut lancer un autre thread
 Un thread peut attendre qu'un ou plusieurs threads se
terminent
 Un thread peut se bloquer et attendre d'être réveillé par un
autre thread
93
Interactions entre threads
 Communication par objet partagé
 Les threads s'exécutent dans la même machine virtuelle, dans le
même espace mémoire
 Accès possible aux mêmes objets
 Modification de l'exemple précédent pour ne plus afficher les résultats
mais les stocker dans un tableau auquel tous les threads ont accès

public class CalculFactorial


{
protected int[] tab;
protected int nb;

public void run(){


int res = 1;
for (int i=1; i<=nb; i++)
res = res * i;
//enregistre le résultat dans tableau
tab[nb - 1] = res;
} 94
Interactions entre threads
 Modification du constructeur pour passer le tableau
partagé en paramètre
public CalculFactoriel(int nb, int[] tab) {
this.nb = nb;
this.tab = tab; }

 Nouveau lancement des threads dans le thread principal


int[] resultats = new int[10];
CalculFactoriel cf;
for (int i=10; i >= 1; i--) {
cf = new CalculFactoriel(i, resultats);
(new Thread(cf)).start();
}
 Avant d'afficher les résultats : doit attendre que tous les
threads soient terminés
95
Interactions entre threads
 Un thread peut attendre qu'un thread se termine via la
méthode join() appellée sur le thread dont on attend la fin
 Pour l'exemple, le thread principal doit attendre que tous les
threads lancés soient terminés
int[] resultats = new int[10];
CalculFactoriel[] tabCF = new CalculFactoriel[10];
CalculFactoriel cf;
// lance les threads
for (int i=10; i>=1; i--) {
cf = new CalculFactoriel(i, resultats);
tabCF[i-1] = cf;
cf.start(); }

// attend la fin de chaque thread


for (int i=0; i < 10; i++) {
try { tabCF[i].join(); }
catch(InterruptedException e) {System.err.println(e);}
}
96
Interactions entre threads
 Modification exemple (suite)
 Une fois la boucle avec les join() passée, on est certain que
tous les threads de calcul sont finis
 Peut alors afficher les résultats

for (int i=1; i<=10; i++)


System.out.println(" factoriel de "
+i+"="+resultats[i-1]);
 Trois méthodes de la classe Thread pour attendre la
terminaison d'un thread
 public void join() : attend la fin du thread
 public void join(int milli) : attend au plus milli
millisecondes
 public void join(int milli, int nano) : attend au
plus milli millisecondes et nano nanosecondes

97
Interactions entre threads
 Méthodes join() (suite)
 Les 3 méthodes join() peuvent lever l'exception
java.lang.InterruptedException
 Si exception levée : signifie que l'attente du thread a été
interrompue et qu'il reprend son activité
 Pour arrêter l'attente d'un thread : appel de la méthode
public void interrupt() sur le thread
 Interrogation sur l'état d'un thread
 public boolean isInterrupted() : retourne vrai si le
thread a été interrompu dans son attente
 public boolean isAlive() : retourne vrai si le thread est
en vie (démarré mais pas encore terminé)

98
Synchronisation sur objets
 Tableau partagé de l'exemple
 Chaque thread écrit dans sa case du tableau
 Pas de risque de conflit dans ce cas
 Mais attention aux accès concurrents à des objets
partagés
 Peut conduire à des incohérences
 Si 2 threads modifient en même temps le même objet par ex.
 En pratique, sur une machine mono-processeur, un seul thread
est actif en même temps
 Mais un thread peut commencer une méthode, passer la main à
un autre thread qui modifiera l'état de l'objet
 Le premier thread reprend alors l'exécution de la méthode avec un
état différent et incohérent
99
Synchronisation sur objets
 Exemple de code pouvant poser problème
public class CalculPuissance {

protected int puissance = 1;

public int calculPuissance(int val) {


int res = val;
for (int i=1; i<puissance; i++)
res = res * val;
return res;
}
public setPuissance(int p) {
puissance = p;
} }
 Doit prendre en compte le cas d'ordonnancement le plus
mauvais
 Ordonnancement préemptif des threads dans ce cas précis
100
Synchronisation sur objets
 Exemple (suite)
 Lancement d'un calcul de puissance
CalculPuissance cp = new CalculPuissance();
... // passage de la référence de cp à d'autres threads
cp.setPuissance(3);
int resultat = cp.calculPuissance(2);
System.out.println(" puissance 3 de 2 = "+resultat);
 Problème
 Si pendant l'exécution de calculPuissance(), un autre thread
appelle setPuissance(), le calcul sera faux !
 Exemple avec un autre thread appelant setPuissance() avec la
valeur 4 pendant l'exécution de calculPuissance()

puissance 3 de 2 = 16
 Valeur 16 renvoyée au lieu de 8 ...
 Car l'attribut puissance est passé à la valeur 4 au milieu de la boucle
101
Synchronisation sur objets
 Primitive synchronized
 Elle s'applique sur un objet (n'importe lequel)
 Exclusion mutuelle sur une séquence de code
 Il est impossible que 2 threads exécutent en même temps une
section de code marquée synchronized pour un même objet
 Sauf si un thread demande explicitement à se bloquer avec
un wait()
 Deux utilisations de synchronized
 Sur la méthode d'une classe (s'applique à tout son code pour un
objet de cette classe)
public synchronized int calculPuissance(int val)
 Sur un objet quelconque
synchronized(cp) {
// zone de code protégée sur l'objet cp
}
102
Synchronisation sur objets
 Retour sur l'exemple
 Suppression de l'erreur potentielle de calcul
 On rajoute synchronized dans la définition des méthodes
public synchronized int calculPuissance(int val) {
int res = val;
for (int i=1; i<puissance; i++)
res = res * val;
return res;
}
public synchronized setPuissance(int p) {
puissance = p;
}
 Il est alors impossible qu'un thread modifie la valeur de
puissance lorsqu'un calcul est en cours
 Car synchronized interdit que setPuissance() soit exécutée
tant que l'exécution d'un calculPuissance() n'est pas finie
103
Synchronisation sur objets
 Exemple du calcul de puissance (suite)
 Il reste un problème potentiel de cohérence, pour la
séquence de lancement du calcul
CalculPuissance cp = new CalculPuissance();
... // passage de la référence de cp à d'autres threads
cp.setPuissance(3);
// un autre thread peut appeler ici setPuissance
// avec la valeur de 4 avant que le calcul soit lancé
cp.setPuissance(4); // exécuté dans un autre thread
int resultat = cp.calculPuissance(2);
System.out.println(" puissance 3 de 2 = "+resultat);

 Le résultat affiché sera là encore 16 au lieu de 8


 Calcul effectué correctement cette fois mais ce n'est pas celui qui
était voulu par le thread !
104
Synchronisation sur objets
 Exemple du calcul de puissance (suite)
 Pour éviter ce problème, il faut protéger la séquence de
positionnement de la puissance puis du calcul
CalculPuissance cp = new CalculPuissance();
int resultat;

... // passage de la référence de cp à d'autres threads

synchronized(cp) {
cp.setPuissance(3);
resultat = cp.calculPuissance(2); }
System.out.println(" puissance 3 de 2 = "+resultat);
 Avec ce code, il est impossible qu'un autre thread exécute sur
l'objet cp la méthode setPuissance() entre le
setPuissance() et le calculPuissance()
105
Synchronisation sur objets
 Exemple du calcul de puissance (fin)
 Avec ce nouveau code, il y a trois sections de code protégées sur
l'objet cp, avec un accès en exécution en exclusion mutuelle
 Le code de la méthode setPuissance()
 Le code de la méthode calculPuissance()
 La séquence
synchronized(cp) {
cp.setPuissance(3);
resultat = cp.calculPuissance(2); }
 Si un thread est en train d'exécuter une de ces 3 sections
protégées sur l'objet cp
 Aucun autre thread ne peut exécuter une des 3 sections protégées tant
que le premier thread n'a pas fini d'exécuter sa section protégée
 Note
 La séquence de code inclue dans le synchronized(cp) {...}
ne contient que des références à cp mais ce n'est pas une obligation
106
Synchronisation sur objets
 Pour des variables de types primitifs (int ...) en
accès concurrent, on utilise volatile
 Le problème n'est pas forcément dans la possible
incohérence en lecture/écriture
 Mais vient du fonctionnement des threads
 Localement, un thread gère une copie d'une variable partagée
 La déclarer comme volatile force à garder la cohérence entre
la copie locale et la variable partagée
 Exemple
protected volatile int nb;

public int incNb() { return nb++; }


 Assure que si un thread exécute incNb() il utilise la valeur
de nb la plus à jour
107
Synchronisation entre threads
 Problème courant
 Besoin d'un point de synchronisation entre threads
 Un thread fait un calcul et un autre thread attend que le résultat de ce
calcul soit disponible pour continuer son exécution
 Solution basique : déclarer un booléen available qui sera mis à vrai quand
le résultat est disponible
public class ThreadCalcul extends Thread {

protected boolean available;


protected int result;

public boolean getAvailable() {


return available;}

public int getResult() { return result; }

public void run() {


// faire calcul, mettre result à jour et préciser
// que le résultat est disponible
available = true;
// continuer l'exécution du thread } } 108
Synchronisation entre threads
 Du coté du thread attendant le résultat
 Solution basique
 Vérifier en permanence la valeur de available

ThreadCalcul calcul;
// calcul lancé avec référence sur bon thread
...
// boucle attendant que le résultat soit disponible
while (!calcul.getAvailable()) {
// fait rien, juste attendre que available change
}
int res = calcul.getResult();
 Problème
 Attente active
 Le thread qui fait la boucle peut ne jamais lacher la main
 L'autre thread ne peut donc pas faire le calcul !
109
Synchronisation entre threads
 Pour éviter problème de l'attente active
 Soit le thread passe la main avec un yield() dans la
boucle
 Mais reste très actif pour pas grand chose ...
 Dans la boucle, le thread peut faire des pauses
 Pause d'un thread : sleep dans la classe Thread
 public static void sleep(long millis[, int nanos])
throws InterruptedException
 Le thread courant fait une pause de millis millisecondes [et
nanos nanosecondes]
 Pendant cette pause, un autre thread peut alors prendre la main
 L'exception InterruptedException est levée si le thread
a été interrompu pendant sa pause
110
Synchronisation entre threads
 Modification de la boucle d'attente avec un sleep
while (!calcul.getAvailable()) {
try {
Thread.sleep(100); }
catch (java.lang.InterruptedException e) { ... }
}
}
int res = calcul.getResult();
 Problèmes
 Combien de temps doit durer la pause ?
 On est pas averti dès que le calcul est fini
 Solution idéale
 Se mettre en pause et être réveillé dès que le résultat est disponible
 Programmation en mode « réactif » : réaction/réveil sur
événements, jamais d'attente ou de vérification active
111
Synchronisation entre threads
 Synchronisation par moniteur
 Dans une section de code protégée par un synchronized,
trois primitives de synchronisation sur un objet
 public void wait() throws InterruptedException
 Le thread se bloque
 Il permet alors à un autre thread d'exécuter une séquence de code
protégée sur l'objet
 C'est le cas où un thread peut exécuter une séquence protégée alors
qu'un autre thread n'a pas terminé son exécution
 Il existe 2 variantes permettant de rester bloquer au plus un certain temps
 public void notify()
 Débloque un thread bloqué (pris au hasard si plusieurs thread
bloqués) sur un wait() sur cet objet
 public void notifyAll()
 Débloque tous les threads bloqués sur un wait() sur cet objet
112
Synchronisation entre threads
 Synchronisation par moniteur
 wait(), notify() et notifyAll() sont des
méthodes de la classe java.lang.Object
 Application à l'exemple précédent
 Thread faisant le calcul
public class ThreadCalcul extends Thread {

protected boolean available;


protected int result;

public synchronized boolean getAvailable() {


return available;
}

113
Synchronisation entre threads
 Thread faisant le calcul (suite)
public void run() {
// faire le calcul, mettre result à jour et préciser
// que le résultat est disponible
synchronized(this) {
available = true;
this.notifyAll();
}// continuer l'exécution du thread }

// opération de récupération du résultat


// si pas encore disponible, on attend qu'il le soit
public synchronized getResult() {
while (!this.getAvailable)
try { this.wait(); }
catch (InterruptedException e) {... } } }
 Thread attendant le résultat
int res = calcul.getResult();
 Si le résultat n'est pas disponible, on sera bloqué en attendant 114
le notify executé par le thread de calcul
 Thread
Résumé sur les threads
 Objet ayant son propre flot d'exécution
 Etats, opérations associés aux threads
 Créé : instantiation standard d'un objet Java
 Démarré et actif : après appel de la méthode start()
 En pause : méthode sleep()
 Bloqué sur un objet synchronisé : méthode wait()
 Réveillé par un notify() sur le même objet
 Attente de la terminaison d'un autre thread : méthode join()
 Interrompu pendant une pause sur un wait(), sleep() ou un
join() par l'appel de interrupt()
 L'exception InterruptedException est levée
 Terminé : arrivé à la fin de sa méthode run()
115
Résumé sur les threads
 Pourquoi et quand utiliser des threads ?
 Sur une machine mono-processeur pas de gain de
performances a priori en parallélisant
 Certains cas imposent des threads
 Pour entreés/sorties, les lectures sont généralement bloquantes
 On dédie un ou plusieurs threads aux réceptions pour gérer des
réceptions multiples, ainsi que d'éviter de bloquer le programme
 Les threads de lecture communiquent par synchronisation/objets
communs avec les autres threads
 Les interfaces graphiques utilisent également des threads,
généralement de hautes priorités
 Peut gérer des événements graphiques (clics ...) venant de l'utilisateur
alors que le programme effectue d'autres traitements
 Peut aussi être une meilleure structuration du
fonctionnement de l'application
116
Résumé sur les threads
 Points délicats en programmation multi-threadée
 Eviter les famines : un thread n'a jamais la main
 Ne jamais utiliser d'attente active
 S'assurer qu'un thread passe la main suffisamment
 Ne pas avoir des threads de haute priorité beaucoup trop actifs
 Eviter les interblocages
 Un thread T1 attend le résultat d'un thread T2 qui attend lui le
résultat du thread T1
 Si chacun se bloque sur un wait(), aucun ne pourra faire le
notify() réveillant l'autre
 Interblocage peut se passer via une chaîne de plusieurs threads
interdépendants
 Pas toujours simple à détecter si dépendances complexes

117
Résumé sur les threads
 Problèmes de performances en cas d'utilisation
massive de threads
 Relativement coûteux de créer un thread à chaque requête
 En temps de création et de destruction par le garbage collector
 Pool de thread : ensemble de threads déjà créés et qui peuvent
être réutilisés pour traiter de nouvelles requêtes
 La méthode run() est une boucle qui traite une requête à chaque
passage
 Avec la synchronisation (wait/notify) on peut relancer un passage
dans la boucle pour traiter une nouvelle requête
 Attention à la taille du pool selon le nombre de requêtes simultanées à
traiter
 Eviter de définir des méthodes en synchronized prenant un
temps relativement long à s'exécuter et étant souvent
appelées
118

Vous aimerez peut-être aussi