Thread
, et en invoquant la méthode
start()
de cet objet ;
Runnable
, en passant cet objet en tant que paramètre de construction d'une instance de
Thread
, et en invoquant la méthode
start()
de cet objet
Thread
.
Executor
. Une implémentation de cette interface prend en charge une implémentation de
Runnable
, et a pour objet de l'exécuter dans un nouveau
Thread
, à un moment ou à un autre.
Cette interface est très simple et ne comporte qu'une seule méthode.
Exemple 83. Interface
Executor
package java.util.concurrent ; public interface Executor { public void execute(Runnable runnable) ; }
ExecutorService
. Cette deuxième interface est plus complexe. Elle expose des méthodes destinées à gérer un service d'exécution, et donc probablement dans la pratique, une réserve de
threads
. L'interface
ExecutorService
étend
Executor
.
Cette interface définit la notion de cycle de vie pour un service d'exécution. Lorsque qu'il est créé, ce service est dans l'état
RUNNING
. Lorsque l'on veut l'éteindre, on le passe dans l'état
SHUTTING_DOWN
par appel à sa méthode
shutdown()
. Dans cet état, il ne peut plus accepter de nouvelles tâches, et le service tente d'éteindre les tâches qu'il est en train d'exécuter. Si l'on veut l'éteindre de façon urgente, alors on fait appel à sa méthode
shutdownNow()
, qui va tout arrêter sans attendre la fin normale de l'exécution des tâches en cours.
ScheduledExecutorService
. Cette troisième interface permet de lancer des tâches périodiques. Ces tâches peuvent se lancer après un certain laps de temps, et être répétées dans le temps à intervalles réguliers. Voyons ses principales méthodes.
schedule(Callable<V> command, long delay, TimeUnit unit)
: crée une tâche qui ne se lancera qu'une seule fois, après le délai indiqué par le paramètre
delay
.
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
: lance la tâche passée en paramètre à intervalles de temps réguliers, indépendamment du temps que cette tâche met à s'exécuter.
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
: lance cette tâche, puis attend le délai
initialDelay
avant de la lancer à nouveau, de façon périodique. On remarquera que cette méthode ne fonctionne pas comme la précédente.
ScheduledFuture<V>
.
Executors
est une classe utilitaire qui expose un jeu de méthodes statiques permettant de gérer l'ensemble des opérations
multithreadées
que nous avons présentées. Les méthodes qu'elle propose peuvent se ranger dans les catégories suivantes.
Executors
permet de créer des réserves de
threads
de différentes façons. Chaque méthode crée sa réserve suivant une certaine sémantique.
newCachedThreadPool()
: crée une réserve capable de créer des nouveaux
threads
à la demande, sans limite. Lorsqu'un
thread
n'est plus utilisé, cette réserve est capable de lui confier de nouvelles tâches. Lorsqu'une nouvelle tâche est confiée à cet
executor
, elle est lancée immédiatement.
newFixedThreadPool(int nThreads)
: crée le même type de réserve que la méthode précédente. La différence est que le nombre maximal de
threads
est fixé à la construction de cet objet. Dans ce cas, il est devient possible qu'une tâche confiée à cette réserve ne puisse pas être exécutée immédiatement. Cette réserve gère donc une file d'attente dans laquelle ces tâches sont placées. Cette file d'attente ne peut pas saturer.
newSingleThreadExecutor()
: crée le même type de réserve que la méthode précédente. Cette fois, la réserve de
threads
ne comporte qu'un unique
thread
. Les tâches qui ne peuvent être immédiatement exécutées sont également placées dans une file d'attente.
ScheduledExecutorService
newScheduledThreadPool(int corePoolSize)
: crée une réserve dont le nombre de
threads
est égal à
corePoolSize
.
newSingleThreadScheduledExecutor()
: crée une réserve de même type que la méthode précédente, fonctionnant avec un unique
thread
.
Executors
.
Exemple 84. Lancement de tâches - pattern
Executors
// dans une méthode main() // création d'un service d'exécution, doté de 4 threads ExecutorService service = Executors.newFixedThreadPool(4) ; // création de 20 futures pour récupérer le résultat // de nos tâches Future<Integer> [] futures = new Future[20] ; for (int i = 0 ; i < futures.length ; i++) { // pour être utilisée dans une classe anonyme, // cette variable doit être final, ce ne peut donc pas être i final int k = i ; System.out.println("Lancement de " + k) ; futures[i] = service.submit(new Callable<Integer>() { public Integer call() throws Exception { // simulation d'un traitement System.out.println("[" + k + "] thread : " + Thread.currentThread().getName()) ; return k ; } }) ; } // autres traitement dans le thread courant // tableau des résultats int [] result = new int [futures.length] ; // récupération des résultats par interrogation des // futures for (int i = 0 ; i < futures.length ; i++) { try { // on donne 100ms à un future pour répondre result[i] = futures[i].get(100, TimeUnit.MILLISECONDS) ; System.out.println(result[i]) ; // gestion des exceptions } catch (TimeoutException e) { result[i] = -1 ; } catch (ExecutionException e) { result[i] = -1 ; } catch (InterruptedException e) { result[i] = -1 ; } } // fermeture du service // sans quoi la JVM reste active service.shutdown() ; System.out.println("Fin du traitement") ;