La programmation concurrente en Java

Slides:



Advertisements
Présentations similaires
La programmation orientée objet avec Java L3-MIAGE Plan
Advertisements

Module Systèmes d’exploitation
Le mécanisme des exceptions
La classe String Attention ce n’est pas un type de base. Il s'agit d'une classe défini dans l’API Java (Dans le package java.lang) String s="aaa"; // s.
Module Systèmes dexploitation Chapitre 6 Communication Interprocessus Partie III École Normale Supérieure Tétouan Département Informatique
GEF 435 Principes des systèmes dexploitation Communication Interprocessus (CIP) II (Tanenbaum 2.3)
TP 7.1 synchronized et join Écrire un programme Java qui crée 1000 threads et maintient un compteur nb du nombre de threads créés jusque-là. Le thread.
Approfondissement du langage
(Classes prédéfinies – API Java)
L’ interruption de boucle
Plan du cours La sérialisation: – comment stocker et restaurer les Objets? Les interfaces graphiques et la programmation évènementielle. –Comment concevoir.
Programmer en JAVA par Tama
TD 1 IJA Introduction Objet, méthode, attribut Classe, instance
Les attributions de valeur (le signe =). Dans cette présentation, nous allons tenter de représenter ce qui se passe dans la mémoire de lordinateur quand.
Introduction à la programmation (420-PK2-SL) cours 15 Gestion des applications Technologie de linformation (LEA.BW)
Système d’Exploitation
Principes de programmation (suite)
Pattern État PowerPoint 2003, télécharger la visionneuse PowerPoint Viewer dernière édition si vous ne lavez pas…télécharger la visionneuse PowerPoint.
Introduction à la programmation (420-PK2-SL) cours 12 Gestion des applications Technologie de linformation (LEA.BW)
JavaBeans Réalise par: EL KHADRAOUY TARIK AOUTIL SAFOWAN.
Les méthodes en java Une méthode est un regroupement d’instructions ayant pour but de faire un traitement bien précis. Une méthode pour être utilisée.
POO-L3 H. Fauconnier1 Chapitre IV 1. classe Object, clonage 2. interfaces 3. classes internes et imbriquées.
Langage Oriente Objet Cours 4.
Introduction au paradigme objet Concepts importants surcharge (overload) redéfinition (override) Définition d’une classe Définition des attributs.
77 Utilisation des classes (suite). 7-2 Objectifs A la fin de ce cours, vous serez capables de : Définir des méthodes surchargées dans une classe Fournir.
1 Objectifs de ce cours (I21) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde Résumé du cours précédent.
Master 1 SIGLIS Java Lecteur Stéphane Tallard Chapitre 5 – Héritage, Interfaces et Listes génériques.
66 Utilisation des classes et des objets. 6-2 Objectifs A la fin de ce cours, vous serez capables de : Créer de nouvelles classes à laide de Eclipse Utiliser.
Une nouvelle structure de données : les tableaux
Présentation Structures de Données et TDA
Programmation concurrente
CSI2520, Hiver 2007 Programmation concurrente. CSI2520, Hiver 2007 Programmation concurrente La programmation est distribuée lorsque les processus ne.
POO-L3 H. Fauconnier1 Supplément gratuit…. POO-L3 H. Fauconnier2 Entrée-sortie public static void main(String[] args) { // sortie avec printf ou double.
Multi-Thread Jian-Yun Nie
IFT 6800 Atelier en Technologies d’information
PROGRAMMATION MULTI-TÂCHES (MULTITHREADING)
1 IFT 6800 Atelier en Technologies dinformation Le langage de programmation Java chapitre 3 : Classes et Objects.
LIFI-Java 2004 Séance du Jeudi 9 sept. Cours 1. La notion de langage Décrire une tâche à effectuer –programme Écrire à un haut niveau –facile pour lutilisateur.
Cours 11 Threads. Chapitre X threads threadPOO-L3 H. Fauconnier3 Threads threads: plusieurs activités qui coexistent et partagent des données exemples:
COURS DE PROGRAMMATION ORIENTEE OBJET :
COURS DE PROGRAMMATION ORIENTEE OBJET :
CSI 1502 Principes fondamentaux de conception de logiciels
Leçon 1 : notion dobjet IUP Génie Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Synchronisation Classique
Structures de données IFT-10541
Les méthodes en java • Une méthode est un regroupement d’instructions ayant pour but de faire un traitement bien précis. • Une méthode pour être utilisée.
1212 Entrée et sortie de fichiers Objectifs À la fin de ce cours, vous serez capables de : • Lire à partir de la console • Écrire sur la console.
LIFI-Java 2004 Séance du Mercredi 22 sept. Cours 3.
Propriétés. Propriétés ► Les propriétés peuvent être visibles dans les environnements de scripts ► Les propriétés peuvent être accédées par programmation.
Gestion de processus Corrigé TD 1 EFREI I
Cours 9 Exceptions (fin) Généricité. POO-L3 H. Fauconnier2 Chaînage d'exceptions  Une exception peut être causée par une autre.  il peut être utile.
Java : Exceptions H Batatia. 5/03/2004Java12: H.Batatia2 Exemple 1 public class Bonjour { public static void main(String[] args) { System.out.println("Bonjour.
11/04/ L'héritage Cours 7 Cours 7.
Cours 7 Classes locales Clonage Divers: tableaux.
Master 1 SIGLIS Java Lecteur Stéphane Tallard Chapitre 6 – Exceptions.
Créer des packages.
Interactions entre Processus
Master 1 SIGLIS Java Lecteur Stéphane Tallard Les erreurs communes en Java.
Tutorat en bio-informatique
Programmation Système et Réseau
Les classes présenté par: RAHMOUNE RIME / ZEKRI SELMA.
Les classes et les objets Les données finales class A { … private final int n = 20 ; // la valeur de n est définie dans sa déclaration … } class A { public.
Schéma de conception Factory Method Exemple Sylvain Giroux.
C# de plus près.  Ce sont globalement les mêmes que Java : ◦ Int(int16, int32), float, double, bool,…  Les classe « communes » sont également les mêmes.
Cours du 5 novembre.
Cours 4 (14 octobre) Héritage. Chapitre III Héritage.
Introduction au langage C : Structures de contrôle 1 ère année Génie Informatique Dr Daouda Traoré Université de Ségou
6ième Classe (Mercredi, 17 novembre) CSI2572
Héritage Conception par Objet et programmation Java
Transcription de la présentation:

La programmation concurrente en Java Les Threads

Le problème (1) Java permet le multitasking Il le fait via les threads (processus légers) Divers threads partagent le même espace mémoire mais pas les registres Quand la JVM donne la main à un thread, il load ses registres et stocke ceux du thread précédent. Exemple : thread1 thread2 compte1.virer(100, compte2); compte2.virer(100, compte1);

Le problème (2) En code exécutable, cela donne pour le premier thread : R1 = compte1.solde; R1 -= 100; compte1.solde = R1; R1 = compte2.solde; R1 += 100; compte2.solde = R1

Le problème (3) L’exécution des 2 threads pourra donner : thread 1 thread2 R1 = compte1.solde; R1 -= 100; compte1.solde = R1; R1 = compte2.solde R1 = compte2.solde; R1 += 100; compte2.solde = R1 compte2.solde = R1; compte1.solde = R1

Le problème (4) Après l’exécution le compte1 retrouve le même solde que précédemment.  Par contre le compte2 voit son solde diminué de 100 !  Il aurait fallu que le retrait et le dépôt soient des opérations atomiques.

Créer un Thread (1) 1ère méthode : Définition : Exécution : public class MonThread extends Thread { public void run() { ….. } Exécution : Thread thread = new MonThread(); thread.start(); Attention : ne pas appeler run(); start() exécute run() dans un autre thread (unité d’exécution)

Créer un Thread (2) 2ème méthode : Définition : Exécution : public class MonRunnable implements Runnable { public void run() { ….. } Exécution : Thread thread = new Thread(new MonRunnable()); thread.start(); Attention : ne pas appeler run() Thread est le contrôleur et Runnable la tâche

Cycle de vie Le Thread existe depuis qu’on a appelé son constructeur Avant d’appeler start(), on pourra procéder à des initialisation Après qu’il ait terminé l’exécution de run(), le Thread continue à exister. On pourra en profiter pour récupérer des résultats

Méthodes static Thread currentThread(); static void sleep(long millis); static void yield(); // passe la main static boolean interrupted(); // clear status void run(); void start(); void interrupt(); boolean isInterrupted(); // keep status void join(); // thread.join() attend la fin de thread InterruptedException // clear status

Arrêter un Thread (1) un Thread s’arrête quand il termine l’exécution de run(); Si la décision de terminer est extérieure : 2 manières en testant une condition par interruption

Arrêter un Thread (2) En testant une condition : public class MonThread extends Thread { private volatile boolean fini = false; public void run() { while (! fini) { ….. } public void terminer() { fini = true;

Arrêter un Thread (3) En interrompant le Thread : public class MonThread extends Thread { public void run() { while (! isInterrupted()) { ….. }

Locking Chaque objet possède un lock (un moniteur) Si une méthode ou un bloc est synchronized, il faudra acquérir le lock de l’objet avant de l’exécuter Si le lock est libre, on y va Si le lock est acquis par un autre thread, on attend qu’il se libère Si dans une méthode ou un block synchronized, on appelle une autre méthode synchronized, il ne faudra pas acquérir le lock une deuxième fois

Méthode synchronized public synchronized int getCompteur() { return compteur; } public synchronized increment() { compteur++;

Bloc synchronized public void incrementAfterOneSecond() { try { Thread.sleep(1000); // pas dans le synchronized // car sleep ne perd pas le lock } catch (InterruptedException ie) { } synchronized (this) { compteur++;

Méthode ou bloc static synchronized (1) Une méthode static synchronized ne peut pas, bien évidemment, obtenir un lock sur this. Elle obtient un lock sur un autre objet : celui représentant la classe dans Class. public class CompteTout { private static int cpt = 0; public static synchronized void incr() { cpt++; } ….. acquiert une lock sur CompteTout.class

Méthode ou bloc static synchronized (2) Une méthode non static accédant à cpt devra acquérir le lock sur le même objet donc Faux : public synchronized int getCpt() { return cpt; } Correct : public int getCpt() { synchronized(CompteTout.class) {

volatile Un champ peut être déclaré volatile dans ce cas au moment d’un changement de thread, la JVM stocke un registre contenant cette variable dans la variable elle-même le champ doit être sur 32 bits maximum pas valable pour une adresse (tableau, objet) : ce serait l’adresse qui serait mis à jour pas ce qu’elle désigne évite de mettre synchronized des méthodes qui ne font que des lectures ou des affectations simples (=) du champ pas de ++ ou de += …..

Deadlock (1) Un deadlock arrive si deux ou plusieurs threads tentent d’acquérir un lock détenu par un autre qui ne pourra pas le libérer pour l’une ou l’autre raison exemple : MonDeadlock

Deadlock (2) public class MonDeadlock extends Thread { private boolean fini = false; public synchronized void run() { while (!fini) { System.out.println("je run"); try { Thread.sleep(1000); } catch (InterruptedException e) { } public synchronized void terminer() { fini = true;

Deadlock (3) Solution : public class MonThread extends Thread { private boolean fini = false; public void run() { while (!getFini()) { System.out.println("je run"); try { Thread.sleep(1000); } catch (InterruptedException e) { } private synchronized boolean getFini() { return fini; public synchronized void terminer() { fini = true;

Comment synchroniser (1) Repérer dans vos classes les champs qui peuvent être modifiés et sont accédés par plusieurs threads. mettre synchronized (sur this) les parties des méthodes qui accèdent à ces champs si plusieurs champs d’un même objet sont accédés dans la même méthode se demander si l’ensemble de ces accès doit être atomique si c’est le cas synchroniser (sur this) cet ensemble (synchroniser éventuellement la méthode)

Comment synchroniser (2) si des méthodes d’objets différents (de classes différentes) sont appelées dans une méthode, se demander si l’ensemble de ces accès doit être atomique. si non, ne pas synchroniser les appels des méthodes sur les objets extérieurs : public class Portefeuille { public versement(double montant, Compte c) { synchronized (this) { this.contenu -= montant; } c.dépot(montant) // où dépôt est une méthode synchronized }

Comment synchroniser (3) si oui, ordonner les objets (acquérir les locks toujours dans le même ordre) public void transférer (Portefeuille p, Compte c) { synchronized(p) { synchronized(c) { c.dépot(p.getContenu()); } une autre méthode qui devrait locker les deux même objets le ferait dans le même ordre Ici c’est simple il suffit d’ordonner les classes

Comment synchroniser (4) si des champs d’objets différents (de la même classe) sont accédés dans la même méthode, se demander si l’ensemble de ces accès doit être atomique. si non, synchroniser séparément les accès des champs des divers objets public virement(double montant, Compte c) { synchronized (this) { this.solde -= montant; } synchronized(c) { c.solde += montant; } }

Comment synchroniser (5) si oui, ordonner les objets sur lesquels on synchronise : par exemple si on a public static int cpt = 0; public int monCpt = 0; public synchronized static incr() { cpt++; } public synchronized static decr() { cpt--;

Comment synchroniser (6) si on veut que l’ensemble des deux incrémentations soit atomique, on fera : public synchronized void incrémenter() { synchronized (CompteTout.class) { incr(); monCpt++; } on a acquis d’abord le lock sur this puis sur CompteTout.class une autre méthode (décrémenter, par exemple) devrait les acquérir dans le même ordre

Comment synchroniser (7) Le cas le plus difficile est quand on doit synchroniser deux objets de la même classe Il faut alors ordonner ces objets parfois un champs de l’objet le permet sinon on utilisera System.identityHashCode()

Comment synchroniser (8) Si on doit locker les deux comptes lors d’un virement on pourra utiliser le numéro de compte: public virement(double montant, Compte c) { if (this.numCompte < c.numCompte) synchronized (this) { synchronized(c) { this.solde -= montant; c.solde += montant; } } else synchronized (c) { synchronized(this) { }

Attendre une ressource La méthode wait() héritée d’Object Doit être appelée sur un objet dont on a acquis le lock Donc dans une méthode synchronized ou un bloc synchronized sur cet objet le thread courant est placée sur une file d’attente liée à cet objet il libère le lock sur l’objet durant le wait();

Prévenir de la disponibilité d’une ressource Les méthodes notify() et notifyAll() héritées d’Object Doit être appelée sur un objet dont on a acquis le lock Donc dans une méthode synchronized ou un bloc synchronized sur cet objet notify() libère un thread en attente sur la file liée à l’objet notifyAll() les libère tous mais un seul passera

Bon usage de wait/notify notify réveille un thread, mais pas nécessairement le bon Il vaut mieux dans la plupart des cas utiliser notifyAll Ne pas faire if (! condition) wait(); si on est réveillé alors que condition n’est pas vraie on cesse d’attendre Faire while (! condition) wait();

Double wait() Ne pas faire : wait(); wait(); Car deux notify() pourraient se produire avant de sortir du premier wait(); le 2nd notify est alors perdu Faire while (nombreDeNotify < 2) wait();

Être sur de notifier Pour être certain que le notify soit fait et qu'on libère bien ceux qui attendent même en cas de problème, toujours faire le notify dans un bloc finally try { … } finally { notifyAll(); // ou notify(); selon les cas }

Double-checked Locking : NON Comment synchroniser une lazy initialization exemple (un singleton) : public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; }

Double-checked Locking : NON le double-checked locking ne fonctionne pas : public static Singleton getInstance() { if (instance == null) synchronized(Singleton.class) { instance = new Singleton(); } return instance; un autre thread risque de ne pus trouver instance à null avant la fin de l’initialisation !

Double-checked Locking : NON Solution : synchroniser la méthode public static synchronized Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; }

wait(), sleep() et synchronisation On essaiera de libérer tous les moniteurs (et autres locks) avant de faire un wait() (sauf celui de l’objet sur lequel on wait();). Il y a un énorme risque de deadlock à ne pas le faire. l’usage de variable locale est parfois utile, celles-ci ne devant pas être synchronisées Dans le même ordre d’idée, on libérera tous les moniteurs, locks, … avant de faire un sleep();

Timer et TimerTask (1) Dans un contexte non graphique n’utilisez pas javax.swing.Timer !!! Ici aussi, Timer est le contrôleur et TimerTask, la tâche class Tâche extends TimerTask { public void run() { System.out.println("action !"); }

Timer et TimerTask (2) Timer timer = new Timer(); timer.schedule(new Tâche(), 5000); // la tâche s’exécutera dans 5 secondes timer.schedule(new Tâche(), 5000, 1000); // la tâche s’exécutera dans 5 secondes puis toutes les // secondes (en temps relatif, décalage possible) timer.scheduleAtFixedRate(new Tâche(), 5000, 1000); // secondes (en temps absolu, non exécution possible) timer.cancel() // plus d’exécution après ça

Concurrence en Java 5.0 Le package java.util.concurrent fournit des classes utilitaires : Semaphore CyclicBarrier CountDownLatch Exchanger diverses implémentation de l’interface BlockingQueue l’interface Callable FutureTask Executor, ExecuterService et ThreadPoolExecutor

BlockingQueue boolean add(E e) -> IllegalStateException si plein E remove() -> NoSuchElementException si vide boolean offer(E e) -> false si plein E poll() -> null si vide void put(E e) -> attend si plein E take() -> attend si vide

BlockingQueue : producteur-consommateur (1) public class Producteur implements Runnable { private BlockingQueue<Integer> queue; private Random random = new Random(); public Producteur(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { while (true) { try { queue.put(random.nextInt(1000)); } catch (InterruptedException e) {

BlockingQueue : producteur-consommateur (2) public class Consommateur implements Runnable { private BlockingQueue<Integer> queue; public Consommateur(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { while (true) { try { System.out.println(queue.take()); } catch (InterruptedException e) {

BlockingQueue : producteur-consommateur (3) import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class Main { public static void main(String[] args) { BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5); new Thread(new Producteur(queue)).start(); new Thread(new Consommateur(queue)).start(); }

Semaphore : théorie Semaphore( int nombreDePermis); // 1 == binaire Semaphore (int nombreDePermis, boolean FIFO); void acquire(); // aussi avec un nbPermis void acquireUninterruptibly(); // aussi avec un nbPermis void release(); // aussi avec un nbPermis boolean tryAcquire(); // aussi avec un nbPermis boolean tryAcquire(long timeout, TimeUnit unités); // aussi avec un nbPermis (en 1er paramètre)

Semaphore (1) public class Noeud implements Cloneable { private int valeur; private Noeud suivant; private Semaphore semaphore = new Semaphore(1); public Noeud(int valeur) { this(valeur, null); } public Noeud(int valeur, Noeud suivant) { this.valeur = valeur; this.suivant = suivant; public Noeud getSuivant() { return suivant;

Semaphore (2) public void setSuivant(Noeud suivant) { this.suivant = suivant; } public int getValeur() { return valeur; public void setValeur(int valeur) { this.valeur = valeur; public Semaphore getSemaphore() { return semaphore;

Semaphore (3) public class Liste { private Noeud tête; public boolean estVide() { return tête == null; } public String toString() { try { String rés = "[ "; for (Noeud n = tête; n != null; n = n.getSuivant()) { n.getSemaphore().acquire(); rés += n.getValeur() + " "; n.getSemaphore().release(); } rés += "]";return rés; } catch (InterruptedException e) { return null; }

Semaphore (4) public boolean ajouterNoeud(int valeur) { Noeud n = new Noeud(valeur); if (estVide()) { tête = n; return true; } try { Noeud prec = null; Noeud noeud = tête; for (; noeud != null; noeud = noeud.getSuivant()) { noeud.getSemaphore().acquire(); if (n.getValeur() == noeud.getValeur()) { if (prec != null) prec.getSemaphore().release(); noeud.getSemaphore().release(); return false; }

Semaphore (5) if (prec == null) { n.setSuivant(noeud); tête = n; if (n.getValeur() < noeud.getValeur()) { if (prec == null) { n.setSuivant(noeud); tête = n; noeud.getSemaphore().release(); return true; } break; if (prec != null) prec.getSemaphore().release(); prec = noeud; n.setSuivant(noeud); prec.setSuivant(n); prec.getSemaphore().release(); if (noeud != null) noeud.getSemaphore().release(); return true; } catch (InterruptedException e) { return false;}

Semaphore (6) public boolean supprimerNoeud(int valeur) { if (estVide()) return false; try { Noeud prec = null; Noeud noeud = tête; for (; noeud != null; noeud = noeud.getSuivant()) { noeud.getSemaphore().acquire(); if (valeur == noeud.getValeur()) { if (prec == null) { tête = tête.getSuivant(); noeud.getSemaphore().release(); return true; }

Semaphore (7) prec.setSuivant(noeud.getSuivant()); noeud.setSuivant(null); prec.getSemaphore().release(); noeud.getSemaphore().release(); return true; } // fin du if == if (prec != null) prec.getSemaphore().release(); prec = noeud; } if (noeud != null) noeud.getSemaphore().release(); } catch (InterruptedException e) { } return false;

Semaphore (8) public class Main { public static void main(String[] args) { final Random random = new Random(); final Liste liste = new Liste(); new Thread() { public void run() { int cpt = 0; while (true) { liste.ajouterNoeud(random.nextInt(10)); System.out.println(++cpt + ") " + liste); } }.start();

Semaphore (9) new Thread() { public void run() { int cpt = 0; while (true) { liste.supprimerNoeud(random.nextInt(10)); System.out.println(++cpt + ". " + liste); } }.start();

CyclicBarrier : théorie CyclicBarrier(int nbParties); CyclicBarrier(int nbParties, Runnable action); int await(); // à faire nbParties fois pour qu’action démarre (existe aussi avec un timeout) reset(); // pour réutiliser la barrière

CyclicBarrier (1) public class SommeLigne extends Thread { private int[] ligne; private int[] résultat; private int indice; private CyclicBarrier barrier; public SommeLigne(CyclicBarrier barrier, int[] ligne, int[] res, int idx) { this.barrier = barrier; this.ligne = ligne; this.résultat = res; this.indice = idx; } public void run() { for (int i = 0; i < ligne.length; i++) résultat[indice] += ligne[i]; try { barrier.await(); } catch (InterruptedException e) { } catch (BrokenBarrierException e) {

CyclicBarrier (2) public class Somme implements Runnable{ private int[] résultats; private int somme; public Somme(int[] résultats) { this.résultats = résultats; } public void run() { for (int i = 0; i < résultats.length; i++) somme += résultats[i]; System.out.println("La somme vaut : " + somme);

CyclicBarrier (3) public class Main { public static void main(String[] args) { int [][] matrice = { { 1 }, { 2, 2 }, { 3, 3, 3 }, { 4, 4, 4, 4 }, { 5, 5, 5, 5, 5 } }; int nbLignes = matrice.length; int [] résultats = new int[nbLignes]; CyclicBarrier barrier = new CyclicBarrier(nbLignes, new Somme(résultats)); for (int i = 0; i < nbLignes; i++) new SommeLigne(barrier, matrice[i], résultats, i).start(); }

CountDownLatch : théorie CountDownLatch(int compteur); void await(); // attends qu’on ait fait compteur countdown() (existe aussi avec un timeout) void countdown(); // à faire compteur fois pour terminer

CountDownLatch (1) public class SommeLigne extends Thread { private int[] ligne, résultat; private int indice; private CountDownLatch startLatch, stopLatch; public SommeLigne(CountDownLatch startLatch, CountDownLatch stopLatch, int[] ligne, int[] res, int idx) { this.startLatch = startLatch; this.stopLatch = stopLatch; this.ligne = ligne; this.résultat = res; this.indice = idx; } public void run() { try { startLatch.await(); } catch (InterruptedException e1) { } for (int i = 0; i < ligne.length; i++) résultat[indice] += ligne[i]; stopLatch.countDown();

CountDownLatch (2) public class Main { public static void main(String[] args) { int [][] matrice = { { 1 }, { 2, 2 }, { 3, 3, 3 }, { 4, 4, 4, 4 }, { 5, 5, 5, 5, 5 } }; int nbLignes = matrice.length; int [] résultats = new int[nbLignes]; CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch stopLatch = new CountDownLatch(nbLignes); for (int i = 0; i < nbLignes; i++) new SommeLigne(startLatch, stopLatch, matrice[i], résultats, i).start(); startLatch.countDown(); try { stopLatch.await(); } catch (InterruptedException e) { } new Thread(new Somme(résultats)).start(); }

Callable <V> Semblable à Runnable ne définit pas de run() mais une méthode V call(); on l’utilisera avec une FutureTask

Callable<Integer> public class SommeCall implements Callable<Integer> { private int[] résultats; private int somme; public SommeCall(int[] résultats) { this.résultats = résultats; } public Integer call() throws Exception { for (int i = 0; i < résultats.length; i++) somme += résultats[i]; return somme;

FutureTask<V> : théorie implémente Runnable et Future<V> FutureTask<V>(Callable<V> callable); FutureTask<V>(Runnable runn, V résultat); void run() appelle call() de callable ou run() de runn V get() attend la fin de run puis renvoie le résultat de call ou celui passé en paramètre isDone(); cancel(); isCancelled();

FutureTask<Integer> (1) utilisation avec une CyclicBarrier : FutureTask ft = new FutureTask<Integer>( new SommeCall(résultats)));; barrier = new CyclicBarrier(nbLignes, ft); for (int i = 0; i < nbLignes; i++) new SommeLigne(barrier, matrice[i], résultats, i).start(); try { System.out.println("la somme = " + ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { }

FutureTask<Integer> (2) utilisation seule après l’emploi d’un CountDownLatch par exemple : FutureTask ft = new FutureTask<Integer>( new SommeCall(résultats)); ft.run(); // ou new Thread(ft).start(); try { System.out.println("la somme = " + ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { }

Exchanger<V> : théorie V exchange(V valeur) attend qu’un autre thread appelle exchange sur cet Exchanger et échange les valeur des deux threads existe aussi avec un timeout

Exchanger<List<Integer>> Un producteur remplit un buffer d’entier. Quand il est plein, il échange ce buffer avec un consommateur. Un consommateur vide un buffer plein Quand il est vide il échange son buffer avec celui du producteur Voir code sur l’extranet

Executor et ExecutorService : théorie Executor : Interface qui ne définit qu’une seule méthode void execute(Runnable runnable); ExecutorService : Interface, extends Executor <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks); <T> List<Future<T>> invokeAny(Collection<Callable<T>>tasks); <T> Future<T> submit(Callable<T> task); Future<?> submit(Runnable task); <T> Future<T> submit(Runnable task, T résultat); void shutdown();

Executor, ExecutorService : exemple class NetworkService { private final ServerSocket serverSocket; private final ExecutorService pool; public NetworkService(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void serve() { try { for ( ; ; ) pool.execute(new Handler(serverSocket.accept())); } catch (IOException ex) { pool.shutdown(); }

java.util.concurrent.locks définit les interfaces Lock Condition ReadWriteLock fournit les implémentations ReentrantLock ReentrantReadWriteLock

Lock : théorie C'est une interface void lock(); // acquiert le lock void lockInterruptibly(); // de manière imterruptible Condition newCondition(); boolean tryLock(); // acquiert le lock s'il est libre boolean tryLock(long time, TimeUnit unit); // s'il devient libre dans le temps indiqué void unlock(); // libère le lock Elle est implémentée par la classe ReentrantLock Exemple : la classe Buffer dans producerconsumer

ReadWriteLock : théorie Cette interface fournit 2 méthodes : Lock readLock(); Lock writeLock(); Une classe qui l'implémente devra garder 2 locks l'un partageable pour les opérations de lecture l'autre exclusif pour les opérations d'écriture Java fournit une implémentation : la classe ReentrantReadWriteLock. Celle-ci possède 2 classes emboîtées : ReentrantReadWriteLock.ReadLock ReentrantReadWriteLock.WriteLock

ReentrantReadWriteLock : théorie (1) on ne peut acquérir le readLock que si le writeLock est libre on ne peut acquérir le writeLock que si le writeLock et le readLock sont libres si on a le writeLock on peut acquérir le readLock si on a le readLock, on ne peut pas acquérir le writeLock, on doit d'abord libérer le readLock si des lecteurs ont le readLock et qu'un rédacteur demande le writeLock, plus aucun autre lecteur n'obtiendra le readLock

ReentrantReadWriteLock : théorie (2) ReentrantReadWriteLock(boolean fair) Si le lock est construit avec fair == true, quand un writeLock est libéré, le writeLock sera accordé au rédacteur qui l'a demandé depuis le plus longtemps, à moins qu'il n'y ait des lecteurs qui attendent depuis encore plus longtemps auquel cas l'ensemble des lecteurs acquérra le readLock Exemple : RWLockCoordinator de readerwriter

Prévention des deadlocks Un deadlock aura lieu uniquement si 4 conditions sont réalisées : Exclusion mutuelle Non préemption Hold and Wait Circularité Pour prévenir un deadlock, on doit soit empêcher la circularité, par exemple en ordonnant les ressources soit empêcher le Hold and Wait, par exemple en acquérrant toutes les ressources simultanément ou en utilisant le Two Phase Locking Exemples : le dîner des philosophes : voir philosophes