:: Enseignements :: Master :: M1 :: 2012-2013 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
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.
-
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.
-
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 ?
-
É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().
-
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.
-
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.
-
Créer un ExecutorService en utilisant la classe
Executors permettant de distribuer le calcul
sur 5 threads différents.
-
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 ?
-
Utiliser le mécanisme de Future pour calculer la somme totale.
-
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());
}
}
© Université de Marne-la-Vallée