Java/Threads

Un article de Agora2ia.



Sommaire

Sources


Overview

En 2004 Java 5 a introduit un nouveau moyen d'exécuter les tâches en parallèles : le framework Executor.

Qu'est-ce qu'une politique d'exécution ? Et bien c'est relativement simple. La politique d'exécution répond globalement à 3 questions :

  • Quelles tâches vont être exécutées ?
  • Dans quel ordre ?
  • Combien de ses tâches peut on exécuter en parallèle ?


Runnable

new Thread(new Runnable {
  public void run() {
    // do it!
  }
}).start();


public interface Runnable {
    public void run();
}

Cette interface souffre de deux limitations:

  • La méthode run() ne peut renvoyer aucune valeur(void)
  • Vous ne pouvez lancer aucune exception


Callable

public interface Callable<V> {
    public V call() throws Exception;
}



Executor

public interface Executor {
   void execute(Runnable command);
}


3 interfaces à connaitre :

  • Executor : Pour l'exécution des "Runnables"
  • ExecutorService : Pour l'exécution des tâches "Runnables" et "Callables"(les Callables seront expliqués par la suite)
  • ScheduledExecutorService : Pour l'exécution des tâches périodiques (qui se répète dans le temps) et différée (la tâche doit commencer dans 60 secondes par exemple)
  • ScheduledExecutorService extends ExecutorService extends Executor


Executors. Notez bien Executors; C'est une classe qui contient toutes les méthodes statiques nécessaire à la création d'objets du framework Executor.


Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new MonRunnable());


ExecutorService execute = Executors.newFixedThreadPool(10);
for(Runnable r : runnables){
    service.execute(r);
}
service.shutdown();


ScheduledExecutorService execute = Executors.newSingleThreadScheduledExecutor();
execute.scheduleAtFixedRate(new MonRunnable(), 0, 1, TimeUnit.SECONDS);


Avec Runnable ou Callable

Avec Runnable :

Executors.newSingleThreadExecutor().execute(new MonRunnable());


Avec Collable :

Executors.newSingleThreadExecutor().submit(new MonCallable());


Future

La methode submit(Callable<V> callable); renvoie une instance de l'interface Future<V>.

Plus précisément il s'agit d'une instance de FutureTask.

Un Future<V> représente le cycle de vie d'une tâche.

Future<Integer> future = Executors.newSingleThreadExecutor().submit(new MonCallable());
System.out.println("Resultat :" + future.get());

future.get() est bloquant: l'exécution attend le resultat.

Future lance aussi des exceptions :

try {
    System.out.println("Result: " + future.get());
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}


CompletionService & ExecutorCompletionService

CompletionService utilise les files, c'est à dire le modèle producteurs-consommateurs. C'est un modèle assez simple, les producteurs créent les tâches et les soumettent à une file, les consommateurs retirent la première tâche et l'exécute libérant ainsi une nouvelle place dans la file pour une tâche future.

Deux méthodes :

  • submit
  • take


ExecutorService executor = Executors.newFixedThreadPool(10);
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
Callable<Integer> task = new MyTask(1);
Future<Integer> future = completionService.submit(task);
List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
// ...
for (int i = 0; i < tasks.size(); ++i) {
   try {
      Integer result = completionService.take().get();
      System.out.println(result);
   } catch(ExecutionException ignore) {}
}


CountDownLatch

A java.util.concurrent.CountDownLatch is a concurrency construct that allows one or more threads to wait for a given set of operations to complete.

A CountDownLatch is initialized with a given count. This count is decremented by calls to the countDown() method. Threads waiting for this count to reach zero can call one of the await() methods. Calling await() blocks the thread until the count reaches zero.

// Initialize lock
// [1]
countDownLatch = new CountDownLatch(1);

// [2]
// Code lauchning thread with asynchronous execution
// [4]
countDownLatch.countDown();

// Come back to the main tread and wainting for asynchronous code control point execution :
// [3]
countDownLatch.await();
// [5]

The control points are reached in the order: [1], [2], [3], [4] and [5].