Thread avec valeur de retour (Callable)
Un thread standard ne retourne aucune valeur. Il s'exécute, fait son travail et se termine. Cependant, il pourrait être pratique d'obtenir une valeur de retour à partir d'un thread. Par exemple, si on voulait calculer plusieurs très grands nombres premiers simultanément ou bien d'aller chercher plusieurs valeurs sur plusieurs sites internet, le résultat serait important. Deux morceaux seront alors requis : les Callable
et les ExecutorService
.
L'interface Callable
Un thread standard implémente l'interface Runnable
et remplace la méthode run()
pour s'exécuter. Cette méthode ne retourne rien (type void
). Un thread avec valeur de retour est très semblable, à quelques différences près :
- Elle implémente l'interface
Callable<T>
au lieu deRunnable
- Elle remplace la méthode
<T> call()
au lieu devoid run()
Note
Lorsque <T>
(ou <K, V>
, <E>
, etc.) est utilisé dans la documentation, ça signifie que le type sera variable et dépendant des besoins du programmeur. Par exemple, la signature d'un ArrayList est ArrayList<E>
(ex. new ArrayList<Integer>()
) et celle d'un HashMap
est HashMap<K, V>
(ex. new HashMap<String, Integer>()
).
Ici est un thread qui calcule le N
nombre premier.
Malheureusement, le nombre premier doit est affiché à l'écran et ne peut pas être utilisé à l'extérieur. Pour transformer ce thread en Callable
, il suffit de remplacer son implémentation :
La classe est maintenant prête à être appelée par un ExecutorService.
La classe ExecutorService
Un exécuteur permet d'exécuter un ou plusieurs threads simultanément. Il en gère l'exécution et la quantité à déployer d'un même coup. Un pool de 5 lancera au maximum 5 threads à la fois, jamais plus.
Il existe plusieurs façons de créer un ExecutorService
:
// Créer un service d'un seul thread
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Créer un service avec un pool de 5 threads
ExecutorService executorService = Executors.newFixedThreadPool(5);
// Créer un service avec un pool de 1 threads (équivalent de newSingleThreadExecutor())
ExecutorService executorService = Executors.newFixedThreadPool(1);
On peut lancer les threads un par un avec la méthode submit()
ou plusieurs à la fois s'ils sont mis dans un ArrayList()
.
Lancer un thread avec submit()
Pour lancer un thread un par un, la méthode submit()
retournera un objet de type Future
(semblable à une Promise
en javascript). C'est cet objet qui nous donnera la valeur de retour lorsqu'elle sera disponible.
Lancer plusieurs threads avec invokeAll()
Il est aussi possible de donner une quantité de threads à lancer à un ExecutorService
et lui laisser gérer leur lancement.
En lançant le programme, on remarquera que l'affichage attend que tous les threads soient terminés avant de s'afficher. À la ligne 12, la méthode invokeAll()
attend que tous les threads aient terminés avant de poursuivre.
Note
Pour confirmer que les threads démarrent par bloc de 5, ajoutez un System.out.println()
à la méthode call()
. Vous verrez alors des messages par bloc de 5.