la programmation concurrente en java les threads

77
La programmation concurrente en Java Les Threads

Upload: internet

Post on 03-Apr-2015

136 views

Category:

Documents


6 download

TRANSCRIPT

Page 1: La programmation concurrente en Java Les Threads

La programmation concurrente en Java

Les Threads

Page 2: 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 thread2compte1.virer(100, compte2); compte2.virer(100, compte1);

Page 3: La programmation concurrente en Java Les Threads

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

Page 4: La programmation concurrente en Java Les Threads

Le problème (3)

• L’exécution des 2 threads pourra donner :thread 1 thread2R1 = compte1.solde;R1 -= 100;compte1.solde = R1;

R1 = compte2.soldeR1 -= 100;

R1 = compte2.solde;R1 += 100;compte2.solde = R1

compte2.solde = R1;R1 = compte1.solde;R1 += 100;compte1.solde = R1

Page 5: La programmation concurrente en Java Les Threads

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.

Page 6: La programmation concurrente en Java Les Threads

Créer un Thread (1)

• 1ère méthode :• Définition :

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)

Page 7: La programmation concurrente en Java Les Threads

Créer un Thread (2)

• 2ème méthode :• Définition :

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

Page 8: La programmation concurrente en Java Les Threads

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

Page 9: La programmation concurrente en Java Les Threads

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

Page 10: La programmation concurrente en Java Les Threads

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

Page 11: La programmation concurrente en Java Les Threads

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;

}

}

Page 12: La programmation concurrente en Java Les Threads

Arrêter un Thread (3)

• En interrompant le Thread :

public class MonThread extends Thread {

public void run() {

while (! isInterrupted()) {

…..

}

}

}

Page 13: La programmation concurrente en Java Les Threads

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

Page 14: La programmation concurrente en Java Les Threads

Méthode synchronized

• public synchronized int getCompteur() {

return compteur;

}

public synchronized increment() {

compteur++;

}

Page 15: La programmation concurrente en Java Les Threads

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++;

}

}

Page 16: La programmation concurrente en Java Les Threads

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

Page 17: La programmation concurrente en Java Les Threads

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 doncFaux :public synchronized int getCpt() {

return cpt;}

Correct :public int getCpt() {

synchronized(CompteTout.class) { return cpt;}

}

Page 18: La programmation concurrente en Java Les Threads

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 += …..

Page 19: La programmation concurrente en Java Les Threads

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

Page 20: La programmation concurrente en Java Les Threads

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; }}

Page 21: La programmation concurrente en Java Les Threads

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; }}

Page 22: La programmation concurrente en Java Les Threads

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)

Page 23: La programmation concurrente en Java Les Threads

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}

Page 24: La programmation concurrente en Java Les Threads

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

Page 25: La programmation concurrente en Java Les Threads

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; }

}

Page 26: La programmation concurrente en Java Les Threads

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--;

}

Page 27: La programmation concurrente en Java Les Threads

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

Page 28: La programmation concurrente en Java Les Threads

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()

Page 29: La programmation concurrente en Java Les Threads

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) {

this.solde -= montant; c.solde += montant;

} }

}

Page 30: La programmation concurrente en Java Les Threads

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();

Page 31: La programmation concurrente en Java Les Threads

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

Page 32: La programmation concurrente en Java Les Threads

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();

Page 33: La programmation concurrente en Java Les Threads

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();

Page 34: La programmation concurrente en Java Les Threads

Ê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

}

Page 35: La programmation concurrente en Java Les Threads

Double-checked Locking : NON

• Comment synchroniser une lazy initialization• exemple (un singleton) :

public static Singleton getInstance() {

if (instance == null)

instance = new Singleton();

return instance;

}

Page 36: La programmation concurrente en Java Les Threads

Double-checked Locking : NON

• le double-checked locking ne fonctionne pas :public static Singleton getInstance() {

if (instance == null)synchronized(Singleton.class) { if (instance == null)

instance = new Singleton(); }

return instance;}

• un autre thread risque de ne pus trouver instance à null avant la fin de l’initialisation !

Page 37: La programmation concurrente en Java Les Threads

Double-checked Locking : NON

• Solution : synchroniser la méthodepublic static synchronized Singleton getInstance()

{

if (instance == null)

instance = new Singleton();

return instance;

}

Page 38: La programmation concurrente en Java Les Threads

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();

Page 39: La programmation concurrente en Java Les Threads

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âcheclass Tâche extends TimerTask {

public void run() {

System.out.println("action !");

}

}

Page 40: La programmation concurrente en Java Les Threads

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);

// la tâche s’exécutera dans 5 secondes puis toutes les

// secondes (en temps absolu, non exécution possible)

timer.cancel() // plus d’exécution après ça

Page 41: La programmation concurrente en Java Les Threads

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

Page 42: La programmation concurrente en Java Les Threads

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

Page 43: La programmation concurrente en Java Les Threads

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) { } } }}

Page 44: La programmation concurrente en Java Les Threads

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) { } } }}

Page 45: La programmation concurrente en Java Les Threads

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(); }}

Page 46: La programmation concurrente en Java Les Threads

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)

Page 47: La programmation concurrente en Java Les Threads

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; }

Page 48: La programmation concurrente en Java Les Threads

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; }}

Page 49: La programmation concurrente en Java Les Threads

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; } }

Page 50: La programmation concurrente en Java Les Threads

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; }

Page 51: La programmation concurrente en Java Les Threads

Semaphore (5) 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;} }

Page 52: La programmation concurrente en Java Les Threads

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; }

Page 53: La programmation concurrente en Java Les Threads

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; } prec.getSemaphore().release(); if (noeud != null) noeud.getSemaphore().release(); } catch (InterruptedException e) { } return false; }

Page 54: La programmation concurrente en Java Les Threads

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();

Page 55: La programmation concurrente en Java Les Threads

Semaphore (9)new Thread() {

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

Page 56: La programmation concurrente en Java Les Threads

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

Page 57: La programmation concurrente en Java Les Threads

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) { } }}

Page 58: La programmation concurrente en Java Les Threads

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); } }

Page 59: La programmation concurrente en Java Les Threads

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();}

Page 60: La programmation concurrente en Java Les Threads

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

Page 61: La programmation concurrente en Java Les Threads

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(); }}

Page 62: La programmation concurrente en Java Les Threads

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();}

Page 63: La programmation concurrente en Java Les Threads

Callable <V>

• Semblable à Runnable

• ne définit pas de run()

• mais une méthode V call();

• on l’utilisera avec une FutureTask

Page 64: La programmation concurrente en Java Les Threads

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; }}

Page 65: La programmation concurrente en Java Les Threads

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();

Page 66: La programmation concurrente en Java Les Threads

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) { e.printStackTrace(); }

Page 67: La programmation concurrente en Java Les Threads

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) { e.printStackTrace(); }

Page 68: La programmation concurrente en Java Les Threads

Exchanger<V> : théorie

• Exchanger()• 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

Page 69: La programmation concurrente en Java Les Threads

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

Page 70: La programmation concurrente en Java Les Threads

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();

Page 71: La programmation concurrente en Java Les Threads

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(); }

} }

Page 72: La programmation concurrente en Java Les Threads

java.util.concurrent.locks

• définit les interfaces– Lock– Condition– ReadWriteLock

• fournit les implémentations – ReentrantLock– ReentrantReadWriteLock

Page 73: La programmation concurrente en Java Les Threads

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

Page 74: La programmation concurrente en Java Les Threads

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

Page 75: La programmation concurrente en Java Les Threads

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

Page 76: La programmation concurrente en Java Les Threads

ReentrantReadWriteLock : théorie (2)

• ReentrantReadWriteLock() • 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

Page 77: La programmation concurrente en Java Les Threads

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