8. Classes utilitaires de l'API Concurrent

8.1. Introduction

L'API Concurrent introduit de nouvelles classes et de nouveaux concepts particulièrement utiles en programmation concurrente. Examinons ces nouveaux éléments.

8.2. Énumération TimeUnit

Tout d'abord, elle introduit une énumération qui permet de préciser dans quelle unité de temps les délais ou périodes que nous allons utiliser sont écrits. Cette énumération définit sept unités :
  • NANOSECONDS ;
  • MICROSECONDS ;
  • MILLISECONDS ;
  • SECONDS ;
  • MINUTES ;
  • HOURS ;
  • DAYS.

8.3. Interface Callable<V>

Cette interface a été créée pour compléter l'interface Runnable. Elle fonctionne comme celle-ci, sauf que la méthode run() qu'elle définit retourne une valeur générique, de type V, et peut jeter une exception.

8.4. Interfaces Future<V> et RunnableFuture<V>

L'interface Future modélise des tâches qui s'exécutent dans un autre thread que le thread courant. Cette interface ne définit pas comment on peut lancer l'exécution de cette tâche. En revanche, elle définit comment récupérer le résultat de cette tâche, par la méthode get(), qui existe en deux versions.
  • get() : retourne le résultat de la tâche. L'appel à cette méthode ne retourne de résultat que lorsque le calcul est terminé. On dit que cet appel est bloquant.
  • get(long timeout, TimeUnit unit) : retourne le résultat de cette tâche. Si ce résultat n'est toujours pas disponible au bout du temps précisé en paramètre, alors cette méthode jette une exception de type TimeoutException.
Cette interface définit trois autres méthodes :
  • cancel() : permet d'interrompre l'exécution de cette tâche ;
  • isCancelled() : retourne true si cette tâche a été interrompue ;
  • isDone() : retourne true si l'exécution de cette tâche est terminée, et que son résultat est disponible.
L'interface RunnableFuture est une extension de l'interface Future et de l'interface Runnable. Elle n'ajoute pas de méthode à ces deux interfaces. Enfin, l'API Concurrent nous fournit une classe, FutureTask<V>, qui implémente ces deux interfaces. On construit une instance de cette classe en lui passant en paramètre une tâche à effectuer, soit sous forme de Runnable, soit sous forme de Callable<V>. Dans le premier cas, on doit également lui fournir l'objet qui sera retourné par la méthode get(), puisque la méthode run() de Runnable ne retourne rien. Voyons un exemple de fonctionnement de cette classe sur un exemple simple.

Exemple 82. Utilisation de FutureTask<V>

public  static  void main(String... args) {

    // création d'une instance de FutureTask<String>
   FutureTask<String> future =  new FutureTask<String>(
       // création d'une instance de Callable, à l'aide 
       // d'une classe anonyme
       new Callable<String>() {

          public String call()  throws Exception {
             try {
                // on simule de façon simple un calcul
                // qui prend du temps
               Thread.sleep(2000) ;
            }  catch (InterruptedException e) {
                // rien
            }
             // et l'on retourne le nom du thread 
             // dans lequel on se trouve
             return Thread.currentThread().getName() ;
         }
      }
   ) ;

    // un future est une instance de Runnable, on peut 
    // donc le lancer de cette façon
    new Thread(future).start() ;

    // on affiche le nom du thread dans lequel on se trouve
   System.out.println(Thread.currentThread().getName()) ;
   
    // jette ExecutionException
    // puis le résultat de l'exécution de notre tâche
   System.out.println(future.get()) ;
}

L'exécution de ce code affiche les éléments suivants.
 main
 Thread-0
On remarquera que le premier message est affiché immédiatement, alors que le second prend un peu plus de temps. On remarquera enfin que notre tâche a bien été exécutée dans un thread différent de celui dans lequel le code de la méthode main() l'a été. Nous verrons dans la suite que l'on peut lancer des tâches de type Future de façon plus intelligente que ce que nous avons fait.

8.5. Interface ScheduledFuture<V> et RunnableScheduledFuture<V>

Ces interfaces étendent toutes deux Future<V>. ScheduledFuture<V> modélise des tâches prévues pour être exécutées dans le futur, après un certain temps. RunnableScheduledFuture<V> permet en plus à cette tâche d'être exécutée à nouveau, de façon périodique.
Java API avancées
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
API Collection
1. Introduction
2. Interface Collection
2.1. Notion de Collection
2.2. Détail des méthodes disponibles
2.3. Interface Iterator
2.4. Implémentation, exemples d'utilisation
3. Interface List
3.1. Notion de List
3.2. Détail des méthodes disponibles
3.3. Interface ListIterator
3.4. Implémentations, exemples d'utilisation
4. Interface Set
4.1. Notion de Set
4.2. Implémentations HashSet et LinkedHashSet
4.3. Exemples d'utilisation
5. Interface SortedSet
5.1. Notion de SortedSet
5.2. Détails des méthodes disponibles
5.3. Exemples d'utilisation
6. Interface NavigableSet
6.1. Notion de NavigableSet
6.2. Détails des méthodes disponibles
6.3. Exemple d'utilisation
7. Interfaces Queue et Deque
7.1. Notion de file d'attente
7.2. Détail des méthodes disponibles
7.3. Utilisation des interfaces Queue et Deque
8. Tables de hachage
8.1. Notion de table de hachage
8.2. Interface Map
8.3. Interface Map.Entry
8.4. Interface SortedMap
8.5. Interface NavigableMap
8.6. Implémentations
8.7. Exemples d'utilisation
9. Classes utilitaires Collections et Arrays
9.1. Introduction
9.2. Classe Arrays
9.3. Classe Collections
Génériques
1. Introduction
2. Un premier exemple
2.1. Une première classe générique
2.2. Une première méthode générique
3. Contraindre un type générique
3.1. Problème posé
3.2. Contraindre un type générique
4. Implémentation des génériques
4.1. Type erasure
4.2. Types génériques et casts
4.3. Type générique et exception
4.4. Construction d'une instance générique
4.5. Génériques et membres statiques
4.6. Collisions de méthodes génériques
4.7. Implémentation de plusieurs types identiques
5. Type <?>
5.1. Introduction
5.2. Type ? extension d'un type
5.3. Type ? super-type d'un type
Expressions régulières
1. Introduction
2. Mise en œuvre des expressions régulières
2.1. Fonctionnement d'une regexp
2.2. Fonctionnement de l'API en Java
2.3. Un premier exemple
2.4. Classe Pattern
2.5. Classe Matcher
2.6. Utilisation des méthode find() et group()
2.7. Méthodes de remplacement
2.8. Sélection de régions
3. Syntaxe des expressions régulières
3.1. Notion de classe
3.2. Étude d'un cas réel
3.3. Recherche d'un mot précis
3.4. Recherche de deux mots précis
3.5. Recherche d'un mot commençant par une lettre donnée
3.6. Cas de mots comportant des caractères accentués
3.7. Recherche sur les lignes
Introspection
1. Introduction
2. La classe Class
2.1. Utilisation de Class
2.2. Méthodes disponibles
2.3. Remarque sur la propriété Accessible
2.4. Type d'une classe
2.5. Création d'une instance à partir d'un objet Class
2.6. Cas des énumérations
3. Les classes Method et Constructor
3.1. Utilisation de Method
3.2. Utilisation de Constructor
3.3. Méthodes disponibles
3.4. Invocation d'une méthode par introspection
4. La classe Field
4.1. Utilisation de Field
4.2. Méthodes disponibles
4.3. Accès à un champ par introspection
5. La classe Modifier
Programmation concurrente
1. Introduction
2. Lançons nos premiers threads
2.1. Introduction
2.2. Un premier thread, extension de Thread
2.3. Un deuxième thread, implémentation de Runnable
2.4. Remarque sur la méthode Thread.sleep(long)
2.5. Arrêter un thread
3. Concurrence d'accès
3.1. Notion d'état
3.2. Exemple de concurrence d'accès sur un état
3.3. Analyse de la concurrence d'accès
3.4. Solution au problème
3.5. Champs volatile
4. Synchronisation
4.1. Définition d'un bloc synchronisé
4.2. Fonctionnement d'un bloc synchronisé
4.3. Notion de deadlock
4.4. Bonnes pratiques pour la synchronisation de threads
5. Opérations atomiques
5.1. Atomicité d'une opération
5.2. Solutions disponibles
5.3. Variables atomiques
6. Collections synchronisées et concurrentes
6.1. Introduction
6.2. Position du problème
6.3. Solutions proposées
7. Files d'attente
7.1. Introduction, pattern producteur / consommateur
7.2. Interface BlockingQueue<E>
7.3. Implémentations de BlockingQueue
7.4. Exemple de producteur / consommateur
7.5. Arrêter un producteur / consommateur : pilule empoisonnée
8. Classes utilitaires de l'API Concurrent
8.1. Introduction
8.2. Énumération TimeUnit
8.3. Interface Callable<V>
8.4. Interfaces Future<V> et RunnableFuture<V>
8.5. Interface ScheduledFuture<V> et RunnableScheduledFuture<V>
9. Pattern executor
9.1. Notion de réserve de threads
9.2. Interface Executor
9.3. Interface ExecutorService
9.4. Interface ScheduledExecutorService
9.5. Classe Executors
9.6. Pattern de lancement de tâches
10. Classes de contrôle d'accès
10.1. Introduction
10.2. Interfaces Lock et ReadWriteLock
10.3. Notion de verrou réentrant
10.4. Classe RentrantLock
10.5. Classe ReadWriteRentrantLock
11. Sémaphores, barrières et latches
11.1. Introduction
11.2. Notion de sémaphore, classe Semaphore
11.3. Notion de latch, classe CountDownLatch
11.4. Notion de barrière, classe CyclicBarrier