APIs Concurrentes Rémi Forax

Slides:



Advertisements
Présentations similaires
Java EPITECH 2012
Advertisements

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.
Approfondissement du langage
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
1 IFT 6800 Atelier en Technologies dinformation Le langage de programmation Java chapitre 3 : Classes et Objects.
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 n°3 rappels. POO-L3 H. Fauconnier2 Entrée-sortie public static void main(String[] args) { // sortie avec printf ou double a = 5.6d ; double b =
Cours 7 Classes locales Clonage Divers: tableaux.
Entrées / Sorties.
Schéma de conception Factory Method Exemple Sylvain Giroux.
Cours du 5 novembre.
Cours 4 (14 octobre) Héritage. Chapitre III Héritage.
Héritage Conception par Objet et programmation Java
Cours du 26 octobre  Classes internes  Clones POO-L3 H. Fauconnier1.
Stéphane Frénot, Frédéric Laforest, Frédéric Le Mouël IJA 1 IJA – TD 8 Les threads en Java.
C++ Les fonctions. Présentation Utilité : Dès qu'un programme dépasse la centaine de lignes de code, il est pratique de pouvoir le décomposer en plusieurs.
CINI – Li115 1 Semaine 9 Algorithmes de tri ● Introduction ● Tri à bulle ● - principe ● - algorithme ● - efficacité ● Tri par sélection ● - principe, algorithme,
1 Java Avancé Collection Concurrente Rémi Forax
Stéphane Frénot, Frederique Laforest, Frédéric Le-Mouël IJA 1 TD 6 IJA Structures de données JAVA.
Frédéric Le Mouël, Stéphane Frénot, Frédérique Laforest, Tarak Chaari – Dpt TC JAV 1 JAV – TD 8 Les threads en Java.
1 Programmation en C++ Fonctions ● Déclaration et définition de fonctions ● Arguments ● Surcharge ● Arguments optionnels ● Fonctions constantes ● Fonctions.
1 Programmation en C++ C++ de base ● Programme C++ ● Variables, objets, types ● Fonctions ● Namespace ● Tests ● Boucles ● Pointeurs, références.
1 Programmation en C++ C++ de base ● Programme C++ ● Variables, objets, types ● Types et opérations fondamentales ● Tests ● Boucles ● Pointeurs, références.
Java – Threads & Calcul distribué Eric Blaudez 06.
1 Programmation en C++ Cycle de vie ● La vie d'un objet ● Destructeur ● Gestion de mémoire dynamique.
Langages de programmation TP6
JAVA.
Modèle objet : les classes
java : l'héritage (rappel)
Environnement de développement des BD
Programmation en C++ Héritage
Les Exceptions Rémi Forax
Pas de variable globale
Les notions de classe et d'objet
Qu'est-ce que POSIX? Une librairie en langage C
Techniques de décomposition
Chapitre 6 La gestion des Exceptions et les assertions
Master Réseaux et Systèmes Distribués (RSD) Algorithmique des systèmes
I21 Algorithmique et programmation II
Concurrence Opérations atomiques Rémi Forax
Principes de programmation (suite)
11ième Classe (Mardi, 18 novembre) CSI2572
Programmation en C++ Fonctions
Principes de programmation (suite)
Les exceptions Rémi Forax
E) Constructeurs et héritage
Puzzler.
Programmation en C++ Fonctions
Programmation en C++ C++ de base
1 RECURSIVITE PRESENTATION Ch. PAUL ALGORITHMIQUE Présentation de la récursivité.
Programmation Orientée Objet C# El Akel Bouchra ISMONTIC Tanger 2013/2014.
Le Java premiers pas.
Java : Socket et Réseaux
Les exceptions Le mécanisme des exceptions est destiné à permettre aux fonctions profondes d'une bibliothèque de notifier la survenue d'une erreur aux.
Schéma de conception Factory Method Exemple
Programmation par Objets et Java
Principes de programmation (suite)
INTERFACE ET POLYMORPHISME
Listes Chaînées.
STREAMS (et fichiers).
Lecture/Écriture de fichiers (I/O)
Variables et accès en Java
Chapitre 11 Exceptions.
Bases de données Singleton pour la connexion
DICTIONNAIRES (MAPS).
TP N°6: Construction d’un Serveur Multi-Client
TP N°4 Développement d’ une application
TP N°5: Partie I Programme Serveur.
Transcription de la présentation:

APIs Concurrentes Rémi Forax

APIs Concurrentes En Java, il existe déjà des APIs concurrente; à utiliser au lieu de ré-inventer la roue java.lang ThreadLocal ClassValue java.util.concurrent Verrous plus évolué Collections concurrentes Calcul asynchrone

Le problème – trouver le bug ! public class IOUtil { private Scanner scanner; public void open(Path path) { try { scanner = new Scanner(path); } catch (IOException e) { throw new UncheckedIOException(e); } } public String nextLine() { if (!scanner.hasNextLine()) { return null; } return scanner.nextLine(); } public void close() { scanner.close(); } } public static void main(String[] args) { IOUtil io = new IOUtil(); for (String arg : args) { Path path = Paths.get(arg); new Thread(() -> { io.open(path); int count = 0; for (; io.nextLine() != null; count++) ; io.close(); System.out.println(count); }).start(); } }

ThreadLocal<T> Déclarer une variable dont la valeur est spécifique à une thread comme une Map<Thread, T> mais sur la thread courante de façon implicite API T get() renvoie la valeur pour la thread courante set(T t) change la valeur pour la thread courante L’implantation stock la valeur dans java.lang.Thread donc pas besoin de verrou

Avec un ThreadLocal public class IOUtil { private final ThreadLocal<Scanner> scanner = new ThreadLocal<>(); public void open(Path path) { try { scanner.set(new Scanner(path)); } catch (IOException e) { throw new UncheckedIOException(e); } } public String nextLine() { if (!scanner.get().hasNextLine()) { return null; } return scanner.get().nextLine(); } public void close() { scanner.get().close(); scanner.remove(); } } public static void main(String[] args) { IOUtil io = new IOUtil(); for (String arg : args) { Path path = Paths.get(arg); new Thread(() -> { io.open(path); int count = 0; for (; io.nextLine() != null; count++) ; io.close(); System.out.println(count); }).start(); } }

Non mutable, c’est mieux ! public class IOUtil implements Closable { private final Scanner scanner; public IOUtil(Path path) { try { scanner = new Scanner(path); } catch (IOException e) { throw new UncheckedIOException(e); } } public String nextLine() { if (!scanner.hasNextLine()) { return null; } return scanner.nextLine(); } public void close() { scanner.close(); } } public static void main(String[] args) { for (String arg : args) { Path path = Paths.get(arg); new Thread(() -> { try(IOUtil io = new IOUtil(path)) { int count = 0; for (; io.nextLine() != null; count++) ; System.out.println(count); } }).start(); } }

ClassValue<T> Met en cache une valeur spécifique à une classe comme une Map<Class, T> + putIfAbsent On ne peut pas utiliser un objet Class comme clé d’une Map car cela empêche le déchargement de la classe. API T get(Class<?> type) renvoie la valeur pour type, appel computeValue (qu’il faut redéfinir) si la valeur n’a pas été calculée Si la classe est déchargée, la valeur associée disparait

Thread et calcul asynchrone

Problème de l’API java.lang.Thread thread.start() est lent Pour obtenir une valeur calculée par un Runnable, il faut utiliser thread.join() Solutions: On pool les threads: Executor (création à la demande, thread non-détruite après un calcul) Un calcul est un Callable<T> T call() throws Exception Le résultat d’un calcul asynchrone est un Future<T>

Exemple simple BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100); Executor executor = new ThreadPoolExecutor(/* corePoolSize */ 2, /* maximumPoolSize */ 4, /* keep alive */ 1, TimeUnit.MINUTES, /* work queue */ queue); executor.execute(() -> { System.out.println("hello 1"); }); executor.execute(() -> { System.out.println("hello 2"); }); executor.shutdown();

ThreadPoolExecutor RejectionPolicy BlockingQueue execute( ) (4) RejectionPolicy BlockingQueue (3) execute( ) ThreadFactory (2) (1) Worker threads 1. on essaye de donner à une thread libre. 2. on essaye de créer une thread. 3. on stocke dans la queue. 4. on rejete la tâche/met en attente/lève une exception

shutdown() Un ThreadPoolExecutor crée des worker threads qui par défaut sont pas marqués deamon La VM meurt que si il n’existe plus que des threads deamon Si on ne fait rien, le ThreadPoolExecutor maintient la VM en vie, donc le programme ne s’arrête jamais executor.shutdown() Il n’est plus possible de soumettre de nouvelle tâche Une fois toutes les tâches terminées, les worker threads sont arrêtés puis la VM s’arrête

Exemple de calcul BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100); ExecutorService executor = new ThreadPoolExecutor(/* corePoolSize */ 2, /* maximumPoolSize */ 4, /* keep alive */ 1, TimeUnit.MINUTES, /* work queue */ queue); Callable<String> task = () -> "hello executor"; Future<String> future = executor.submit(task); executor.shutdown(); try { System.out.println(future.get()); } catch(ExecutionException e) { // il y a eu une erreur pendant l’exécution du calcul // l’erreur est stockée dans le cause de l’exception throw new RuntimeException(e.getCause()); }

Future<T> Promesse d’une valeur future et façon de contrôler l’exécution la tâche correspondante API Demande la valeur, bloque si le calcul est pas fini T get() Demande l’arrêt de la tâche boolean cancel(boolean callInterruptIfRunning) (revoie false si la tâche est déjà fini)

ExecutorService Interface qui hérite de Executor Soumettre un calcul Future<T> submit(Callable<T>) Soumission de tâche interdite, l’Executor s’arrètera une fois tous les tâches en cours effectuée void shutdown() Soumettre plein de tâches en même temps List<Future<T>> invokeAll(Collection<? extends Callable<T>>) Soumettre plusieurs calculs, garder le premier qui répond, on arrête les autres T invokeAny(Collection<? extends Callable<T>>

Créer un ExecutorService new ThreadPoolExecutor(blah, blah, ...) mais il y a plein de paramètres Utilser les static factory methods Thread pool avec 1 thread unique Executors.newSingleThreadExecutor() Thread pool avec n-threads Executors.newFixedThreadPool(int nThreads) Thread pool qui alloue autant de thread que nécessaire avec un keepAlive infini Executors.newCachedThreadPool()

Exemple de Ping public static void main(String[] args) throws InterruptedException, UnknownHostException { InetAddress host = InetAddress.getByName("www.google.fr"); ExecutorService executor = Executors.newCachedThreadPool(); Future<Long> future = executor.submit(() -> { long time = System.nanoTime(); host.isReachable(2000); return System.nanoTime() - time; }); executor.shutdown(); try { System.out.println("reach " + host + " in " + future.get() + " ns"); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } }

Et avec plusieurs hosts private static Callable<Long> newCallable(InetAddress host) { return () -> { long time = System.nanoTime(); host.isReachable(2000); return System.nanoTime() - time; }; } public static void main(String[] args) throws InterruptedException, ExecutionException { List<InetAddress> hosts = … List<Callable<Long>> callables = hosts.stream().map(host -> newCallable(host)).collect(toList()); ExecutorService executor = Executors.newFixedThreadPool(2); List<Future<Long>> futures = executor.invokeAll(callables); executor.shutdown(); for (int i = 0; i < hosts.size(); i++) { System.out.println("reach " + hosts.get(i) + " in " + futures.get(i).get() + " ns"); } }

CompletableFuture<T> Sorte de Future sous steroid (ils héritent de Future), ils permettent de chaîner les calculs comme avec un Stream Création CompletableFuture.runAsync(Runnable, Executor) CompletableFuture.supplyAsync(Supplier, Executor) Tranformation (équivalent du map) thenApply(Function<T, R>) thenApplyAsync(Function<T, R>, Executor) Accept (opération terminal) thenAccept(Consumer<T>) thenAcceptAsync(Consumer<T>, Executor)

Exemple de CompletableFuture private static Supplier<Long> newSupplier(InetAddress host) { return () -> { long time = System.nanoTime(); host.isReachable(2000); // FIXME catch IOException return System.nanoTime() - time; }; } public static void main(String[] args) throws InterruptedException, UnknownHostException, ExecutionException { InetAddress host = InetAddress.getByName("www.google.fr"); ExecutorService executor = Executors.newCachedThreadPool(); Supplier<Long> supplier = newSupplier(host); CompletableFuture<Long> future = CompletableFuture.supplyAsync(supplier, executor); executor.shutdown(); System.out.println("reach " + host + " in " + future.get() + " ns"); future.thenAccept(elapsedTime -> { System.out.println("reach " + host + " in " + elapsedTime + " ns"); }); }

Verrous améliorés

Verrous amélioré Exchanger Echange 2 valeurs entre 2 threads CountDownLatch/CyclicBarrier Point de RDV entre plusieurs threads Semaphore Verrou avec plusieurs permis ReentrantReadWriteLock 2 locks, un pour la lecture, un pour l’écriture StampedLock Verrou optimiste

Collections concurrentes

Liste concurrente List<Integer> list = ... ExecutorService executor = Executors.newFixedThreadPool(5); for(int n = 0; n < 5; n++) { executor.execute(() -> { for(int i = 0; i < 1_000; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { return; } list.add(i); } }); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS); System.out.println(list.size()); j.u.ArrayList Collections.synchronizedList( new ArrayList()) j.u.Vector j.u.c.CopyOnWriteArrayList

Liste concurrente List<Integer> list = ... ExecutorService executor = Executors.newFixedThreadPool(5); for(int n = 0; n < 5; n++) { executor.execute(() -> { for(int i = 0; i < 1_000; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { return; } list.add(i); } }); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS); System.out.println(list.size()); j.u.ArrayList ?? Collections.synchronizedList( new ArrayList()) 5000 j.u.Vector j.u.c.CopyOnWriteArrayList

Liste concurrente & iteration List<Integer> list = ... new Thread(() -> { for(int i = 0; i < 1_000; i++) { Thread.sleep(1); // FIXME add catch InterruptedException list.add(i); } }).start(); new Thread(() -> { for(;;) { int sum = 0; for(int value: list) { sum += value; } System.out.println(sum); ... } }).start(); j.u.ArrayList Collections.synchronizedList( new ArrayList()) j.u.Vector j.u.c.CopyOnWriteArrayList

Liste concurrente & iteration List<Integer> list = ... new Thread(() -> { for(int i = 0; i < 1_000; i++) { Thread.sleep(1); // FIXME add catch InterruptedException list.add(i); } }).start(); new Thread(() -> { for(;;) { int sum = 0; for(int value: list) { sum += value; } System.out.println(sum); ... } }).start(); j.u.ArrayList CME ! Collections.synchronizedList( new ArrayList()) j.u.Vector j.u.c.CopyOnWriteArrayList OK

Structures Concurrentes j.u.HashMap, TreeMap, HashSet, ArrayList, ArrayDeque Pas concurrente j.u.Hashtable, Vector, Stack Concurrente sauf itération (et deprecated !) Collections.synchronizedSet/List/Map() Concurrente sauf itération j.u.c.ConcurrentHashMap, ConcurrentSkipListMap, CopyOnWriteArraySet, CopyOnWriteArrayList, ConcurrentLinkedDeque Concurrente

java.util.concurrent Toutes les collections de j.u.c sont concurrentes Attention, size() est pas forcément en temps constant (isEmpty() toujours) Deux sémantiques d’itération Snapshot: (CopyOnWrite...) l’iterateur voit les objets présent à la création de l’itérateur Weekly consistent (toutes les autres) l’iterateur peut voir les objets ajoutés après la création de l’itérateur (ou pas).