:: Enseignements :: Master :: M1 :: 2012-2013 :: Java Avancé ::
[LOGO]

Executor et Fork/Join pool


Exercice 1 - Producteur et consommateur

On souhaite écrire un programme permettant de simuler un comportement de producteurs/consommateurs.
Un producteur est un thread qui à intervalle régulier va aller stocker un message dans une file bloquante.
Un consommateur est un thread qui à intervalle régulier va aller chercher un message dans la file bloquante, puis afficher ce message.
On entend par liste bloquante une liste qui, si il n'y a plus de message, met les consommateurs en attente si ceux-ci demandent un message et les réveille lorsqu'un message arrive. De même, elle met les producteurs en attente si la liste est pleine et les réveille lorsqu'une place est libérée, c'est-à-dire, quand un message a été lu.

  1. Nous allons utiliser un java.util.concurrent.ScheduledExecutorService pour ordonnancer dans le temps les producteurs et les consommateurs.
    Rappeler la différence entre les méthodes schedule, scheduleAtFixedDelay et scheduleAtFixedRate de la classe ScheduledExecutorService.
    Puis, créer un ScheduledExecutorService à partir de la classe Executors et créer une tâche qui affiche bonjour toutes les 300 millisecondes.
  2. Puisque les producteurs et les consommateurs peuvent être bloqués par la file contenant les messages, doit-on les schéduler avec scheduleAtFixedDelay ou scheduleAtFixedRate ?
  3. Écrire une méthode créant le Runnable d'un producteur avec en paramètre la chaîne de caractères qui sera insérée par le producteur dans la file bloquante.
    Pour implanter la file bloquante, utiliser la classe java.util.concurrent.LinkedTransferQueue ainsi que ses méthodes put() et take().
  4. Faire la même chose avec un consommateur en prenant en paramètre un entier unique id. Le consommateur, après avoir retiré un message de la file bloquante, affichera celui-ci avec son numéro unique id.
  5. Que pourrait-il arriver dans le cas où la file bloquante est pleine/vide ? Que faire pour éviter le problème ?

Exercice 2 - Travail en parallèle

On souhaite écrire un programme calculant la somme des valeurs d'un tableau de nombre flottants de 1 000 000 éléments. On souhaite paralléliser le programme et permettre d'exécuter en même temps plusieurs calculs, on va donc décomposer la somme en somme intermédiaire effectuer chacune dans une thread et en une somme des sommes intermédiaires pour obtenir la somme totale.
Pour tester, on initialisera le tableau en appelant la fonction Math.sin sur les valeurs de 0 à 999 999.

  1. Créer un ExecutorService en utilisant la classe Executors permettant de distribuer le calcul sur 5 threads différents.
  2. Dans un premier temps, on ne s'intéresse qu'au somme intermédiaire que l'on va afficher. Écrire le programme qui effectue le calcul en parallèle en utilisant des Callable qui effectuent le calcul.
    Penser à utiliser la méthode shutdown pour indiquer qu'il n'y a plus de calculs à effectuer.
    Effectuer un affichage des sommes intermédiaires.
    Que remarque t'on ?
  3. Utiliser le mécanisme de Future pour calculer la somme totale.
  4. On souhaite maintenant utiliser un pool Fork/Join.
    Lisez la doc de java.util.concurrent.ForkJoinPool et modifier le code ci-dessous.
        public class Accumulator extends RecursiveTask<Double> {
      private final double[] array;
      private final int lo;
      private final int hi;
      
      private Accumulator(double[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
      }
    
      @Override
      protected Double compute() {
        int l = lo;
        int h = hi;
        if (h - l > 1024) {
          int mid = (l + h) >>> 1;
          
          // TODO
          return ...
        }
        
        //TODO
        return ...
      }
    
      public static void main(String[] args) throws InterruptedException, ExecutionException {
        ForkJoinPool pool = new ForkJoinPool();
        double[] array = new double[1_000_000];
        for(int i=0; i<array.length; i++) {
          array[i] = Math.sin(i);
        }
        Accumulator a = new Accumulator(array, 0, array.length);
        System.out.println(pool.submit(a).get());
      }
    }