L'intérêt de ce kata est d'apprendre / de revoir la programmation concurrente en java.
Que ce soit en tant que médecin, infirmier ou étudiant en médecine, il n'est pas évident de faire face à une longue file d'attente tout seul afin de vacciner toutes les personnes attendant patiemment leur tour.
Pour ne pas les faire attendre trop longtemps, il est préférable qu'il y ait de multiples personnes affectées à cette tâche.
Par conséquent, on aura le scénario suivant : plusieurs personnels de santé seront chargés de la vaccination et se "partageront" les personnes de la file d'attente. La personne en tête de file se fera vacciner par n'importe quel personnel de santé actuellement disponible. La vaccination prendra entre 1 et 5 secondes (aléatoirement par personne). Mais comme vacciner non-stop est exténuant, ils feront des pauses à intervalle régulier. Ils devront respecter un quota de personnes vaccinées et attendre que les autres atteignent également ce quota avant de pouvoir reprendre.
Par exemple, si le quota est égal à 3, un personnel devra se mettre en pause lorsqu'il aura vacciné trois personnes et attendre que tous les autres aient atteint ce quota avant de pouvoir reprendre (en supposant qu'il y ait assez de personnes restantes sinon ils termineront) et ainsi de suite jusqu'à la fin.
L'objectif est d'implémenter un petit programme simulant ce scénario. Il prendra trois paramètres p, q et t avec :
- p, le nombre de personnels
- q, le quota évoqué précédemment
- t, le nombre de personnes dans la file d'attente
A la fin, le nombre total de personnes vaccinées par chacun des personnels doit être affiché.
Pour la file d'attente, vous pouvez générer un nombre aléatoire de personnes en créant une fonction utilitaire.
Exemples d'affichage possible avec :
p = 2 et q = 3 (avec une file de 4 personnes)
Thread 1 a vacciné Jean.
Thread 1 a vacciné Pierre.
Thread 1 a atteint son quota.
Thread 2 a vacciné Pierre.
Plus de personnes à vacciner.
Thread 1 a vacciné 3 personnes
Thread 2 a vacciné 1 personne
p = 3 et q = 3 (avec une file de 7 personnes)
Thread 1 a vacciné Jean.
Thread 2 a vacciné Paul.
Thread 1 a vacciné Pierre.
Thread 2 a vacciné Marie.
Thread 1 a vacciné Robert.
Thread 1 a atteint son quota.
Thread 3 a vacciné Tom.
Thread 2 a vacciné Anne.
Thread 2 a atteint son quota.
Plus de personnes à vacciner.
Thread 1 a vacciné 3 personnes
Thread 2 a vacciné 3 personnes
Thread 3 a vacciné 1 personnes
Et sinon, voici une solution proposée par l'auteur en Java :
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
class Person {
private String name;
private boolean isVaccinated;
public Person(String name, boolean isVaccinated) {
this.name = name;
this.isVaccinated = isVaccinated;
}
public void vaccinate() throws InterruptedException {
Random random = new Random();
int randomDelay = random.nextInt(5) + 1;
Thread.sleep(1000 * randomDelay);
this.isVaccinated = true;
}
public String getName() {
return this.name;
}
}
class KataProgram {
public static String[] nameArray = new String[]{
"Jean", "Charles", "Gaspard", "Ulysse", "Pierre", "Marie",
"Elisabeth", "Anne", "Jules", "Julien", "Georges",
"Caroline", "Mohammed", "Antoine", "Louis",
"Catherine", "Suzanne", "Alphonse",
"Franck", "Martin", "Robert", "Paul", "Jacques", "Alice",
"Cyrille", "Louise"
};
public static Queue<Person> generatePersonsList(int size) {
Queue<Person> list = new ConcurrentLinkedQueue<Person>();
Random random = new Random();
for (int i = 0; i < size; i++) {
int randomNumber = random.nextInt(nameArray.length);
list.add(new Person(nameArray[randomNumber], false));
}
return list;
}
public static int parseNumberArgument(String arg) {
int number = 0;
try {
number = Integer.parseInt(arg);
} catch (NumberFormatException e){
System.out.println("Each argument must be an integer.");
System.exit(1);
}
return number;
}
public static void main(String[] args) throws InterruptedException {
if (args.length == 3) {
int threadNumber = parseNumberArgument(args[0]);
int quota = parseNumberArgument(args[1]);
int totalPeople = parseNumberArgument(args[2]);
initAndStart(threadNumber, quota, totalPeople);
} else {
System.out.println("Il faut exactement 3 paramètres");
}
}
public static void initAndStart(int threadNumber, int quota, int totalPeople) throws InterruptedException {
AtomicBoolean isOver = new AtomicBoolean(false);
AtomicBoolean endMessageFlag = new AtomicBoolean(false);
ExecutorService executorService = Executors.newFixedThreadPool(threadNumber);
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadNumber);
Queue<Person> personsList = generatePersonsList(totalPeople);
List<Future<String>> resultList;
List<Callable<String>> taskList = new ArrayList<Callable<String>>();
for (int i = 0; i < threadNumber; i++){
Callable<String> callable = new VaccineCallable(cyclicBarrier, personsList, quota, isOver, endMessageFlag);
taskList.add(callable);
}
resultList = executorService.invokeAll(taskList);
executorService.shutdown();
displayThreadsResults(resultList);
}
public static void displayThreadsResults(List<Future<String>> list) {
for (Future<String> future : list){
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
class VaccineCallable implements Callable<String> {
private CyclicBarrier cyclicBarrier;
private Queue<Person> personsList;
private int quota;
private AtomicBoolean isOver;
private AtomicBoolean endMessageFlag;
private Integer vaccinatedNumber = 0;
public VaccineCallable(CyclicBarrier cyclicBarrier, Queue<Person> personsList, int quota, AtomicBoolean isOver, AtomicBoolean endMessageFlag) {
this.cyclicBarrier = cyclicBarrier;
this.personsList = personsList;
this.quota = quota;
this.isOver = isOver;
this.endMessageFlag = endMessageFlag;
}
@Override
public String call() throws Exception {
String currentThreadName = Thread.currentThread().getName();
String shortThreadName = "Thread " + currentThreadName.substring(currentThreadName.lastIndexOf('-') + 1, currentThreadName.length());
while(!this.isOver.get()) {
Person currentPerson = this.personsList.poll();
if (currentPerson != null) {
currentPerson.vaccinate();
this.vaccinatedNumber++;
System.out.println(shortThreadName + " a vacciné " + currentPerson.getName() + ".");
}
// si un personnel a atteint son quota et qu'il y a encore des personnes
if (this.vaccinatedNumber % this.quota == 0 && !this.isOver.get()) {
System.out.println(shortThreadName + " a atteint son quota. En attente...");
try {
this.cyclicBarrier.await(20000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
System.out.println(shortThreadName + " dans " + e.getClass().getSimpleName());
}
}
// s'il n'y a plus de personnes dans la file
if (this.personsList.size() == 0) {
this.isOver.set(true);
if (!this.endMessageFlag.get()) {
this.endMessageFlag.set(true);
System.out.println("Plus de personnes à vacciner.");
}
}
}
return shortThreadName + " a vacciné " + this.vaccinatedNumber + " personne(s)";
}
}
Votre équipe TakiVeille