Date de première publication : 2015/09/29
Présentation de l'objectif
On veut modéliser l'implantation de colons sur une carte. Les colons vont se déplacer aléatoirement sur une carte jusqu'à trouver une ville et s'installer à côté de celle-ci. Les colons, quelle que soit leur origine, apparaissent sur les bords de la carte.
On vous propose de choisir une direction de marche (nord, sud, est, ouest) puis une distance maximale de marche. On répète le tout tant que le colon n'a pas trouvé la ville ou bien sur une borne max (le colon, épuisé, disparait)
Les déplacements des colons peuvent être complètement aléatoires, ou bien orientés (en direction de la ville originale).
Le résultat, l'implantation des colons, sera visualisé sur une interface Swing. Sauf à des fins de débogage, on ne demande pas la visualisation des déplacements des colons
Boite à outils - modélisation - rappels
Faisons une liste rapide des classes dont nous avons besoin :
- Un composant Carte mémorise l'emplacement originel de la ville et l'implantation des colons
- Des colons !!
- Une application principale qui encapsule ou dérive d'un
JFrame
- Un composant (dérivé de
JPanel
) pour dessiner - Vous pouvez également ajouter des composants d'interface : un
JLabel
pour afficher le nombre de colons sur la carte, des boutons ou des menus pour contrôler l'arrivée des colons
Comme souvent, l'aléatoire va jouer un rôle important :
Random random = new Random();
int i = random.netxInt(100);
et on va essayer de faire du multithreading avec les classes Thread
, Runnable
, Executor
et d'autres encores. Pour connaître,
le nombre de processeurs disponibles, on a l'instruction suivante :
System.out.println("Available Processors: "+Runtime.getRuntime().availableProcessors());
et pour calculer les temps d'exécution des programmes, on pourra s'inspirer des lignes suivantes :
long debut = System.currentTimeMillis();
System.out.println("elapsed time: "+(System.currentTimeMillis()-debut));
Première version : Runnable avec ou sans Executor
Pour utiliser le multithreading, on va considérer que le déplacement globlal d'un colon est une tâche à réaliser.
La classe Colon
va donc implémenter l'interface Runnable et proposer la méthode run()
qui va bien.
A la fin de la méthode run()
, on n'oubliera pas de mettre à jour la carte et à rafraichir la zone d'affichage (il faudra donc
paramétrer la classe ou les objets par ces éléments).
Pour exécuter les tâches ainsi définies, il suffit de faire quelque chose comme cela :
Colon colon = new Colon();
Thread t = new Thread(colon);
t.start();
Pour que cela soit plus "propre", nous allons utiliser le framework java.util.concurrent.Executor
.
Les tâches ne sont plus directement associées à un thread mais plutôt à une entité (l'exécutor) qui va ventiler les tâches sur des threads.
Par exemple, si la machine dispose de 4 processeurs, on peut créer un exécutor avec 4 threads d'exécution associés à une file commune de tâches.
ExecutorService es = Executors.newFixedThreadPool(4);
es.execute(colon);
Quand toutes les tâches ont été envoyées, on interdit à l'exécutor d'accepter de nouvelles tâches :
es.shutdown();
et on peut être notifié de la fin de toutes les tâches confiées, avec par exemple :
es.awaitTermination(2, TimeUnit.SECONDS);
Je vous propose de coder tout cela et de comparer les temps d'exécution en jouant avec le nombre de threads de l'exécutor.
La méthode getActiveCount()
de l'exécutor permet de conaître le nombre de threads réellement utilisé
Version 2
Le problème c'est que la méthode run()
proposée par l'interface Runnable
ne permet
pas de récupérer des informations, ce que permet la méthode call()
de l'interface Callable
.
Si on change le type de Colon, le code précédent marche en appelant la méthode submit()
de l'exécutor au lien de execute()
.
Pour exploiter les "tâches" une fois qu'elles sont terminées, nous allons utiliser un service de complétion auquel on soumettre maintenant les
"tâches" (méthode submit()
)
CompletionService<Colon2> cs = new ExecutorCompletionService<Colon>(es);
Le service de complétion permet grâce à la méthode take()
de récupérer la tâche réalisée (placée dans une file).
La tâche est de type Future
, c'est-à-dire un résultat dont l'état peut être effectué, abandonné, pas encore fait ....
La méthode get()
d'un objet Future
est bloquante, jusqu'à ce que le résultat soit disponible.
Par exemple, cela m'a permis de sortir le rafraichissement de la classe Colon
pour le placer dans le programme principal :
while (cs.take().get()!=null) repaint();
Aller plus loin ...
On peut faire des statistiques sur les colons : déplacement moyen, taux de réussite, ....
On peut aussi compléter l'interface graphique pour permettre de zoomer sur une carte relativement grande (C'est simple et direct
avec les Graphics2D
)
On pourrait afficher en temps réél les déplacements des colons