Signaux

Rendez-vous (come back)

Dans cet exercice, on reprend le dernier exercice de la séance précédente. On rappelle qu'on souhaite réaliser une classe RendezVous thread-safe qui offre un méthode set permettant de proposer une valeur et une méthode get qui ''bloque'' jusqu'à ce qu'une valeur ait été proposée et la renvoie lorsque c'est le cas.

Écrire la classe RendezVous sans que votre code ne fasse de l'attente active.

Tester votre classe avec le main ci-dessous. Vous devriez observer avec la commande top que votre programme ne consomme quasiment aucun temps processeur.

public static void main(String[] args) throws InterruptedException {
    var rdv = new RendezVous<String>();
    Thread.ofPlatform().start(() -> {
      try {
        Thread.sleep(20_000);
        rdv.set("Message");
      } catch (InterruptedException e) {
        throw new AssertionError(e);
      }
    });
    System.out.println(rdv.get());
  }

Comparer le résultat de la commande top avec le même main mais en utilisant la classe RendezVous de la semaine dernière qui fait de l'attente active.

File d'attente thread-safe

Dans cet exercice, on veut créer une file d'attente thread-safe.

Dans un premier temps, on cherche a réaliser une file d'attente thread-safe non-bornée que nous appellerons UnboundedSafeQueue<V>. Cette classe offrira deux méthodes :

Écrire la classe UnboundedSafeQueue<V>.

Écrire un main qui démarre 3 threads qui, toutes les 2 secondes, vont ajouter leur nom (utiliser getName()) dans une UnboundedSafeQueue. Le main effectuera une boucle qui récupère en permanence le premier élément de la UnboundedSafeQueue et l'affiche.

Nous voulons maintenant réaliser une file d'attente thread-safe de taille bornée BoundedSafeQueue<V>. La capacité maximale est fixée à la création de la file et la taille de la file ne devra jamais dépasser cette capacité. La méthode add n'est donc plus adaptée car elle ne prend pas en compte la capacité maximale. On la remplace par une méthode put(V value) qui ajoute value s'il y a la place et sinon attend jusqu'à ce qu'une place se libère.

Écrire la classe BoundedSafeQueue<V> et la tester avec différentes valeurs de capacité initiale.

Vérifier que l'on observe le comportement attendu quand on augmente le nombre de threads en jeu (10, 50, ...).

Vote (à la maison)

Dans cet exercice, on veut réaliser une classe Vote. Cette classe thread-safe permet d'enregistrer n votes pour des String. Quand les n votes ont été enregistrés, on renvoie à chaque votant la String qui a reçu le plus de votes (en cas d'égalité, on renvoie la plus petite dans l'ordre alphabétique ayant reçu le plus de votes). Le nombre n de votes attendus est pris en paramètre par le constructeur.

La classe Vote offre une seule méthode vote qui sert à proposer son vote et qui bloque jusqu'à ce que les n votes soient arrivés. Ensuite elle renvoie le gagnant. Si vote est appelée après que les n votes aient été reçus, la méthode renvoie simplement le gagnant.

Le main ci-dessous donne un exemple d'utilisation de la classe.

  public static void main(String[] args) throws InterruptedException {
    var vote = new Vote(4);
    Thread.ofPlatform().start(() -> {
      try {
        Thread.sleep(2_000);
        System.out.println("The winner is " + vote.vote("un"));
      } catch (InterruptedException e) {
        throw new AssertionError(e);
      }
    });
    Thread.ofPlatform().start(() -> {
      try {
        Thread.sleep(1_500);
        System.out.println("The winner is " + vote.vote("zero"));
      } catch (InterruptedException e) {
        throw new AssertionError(e);
      }
    });
    Thread.ofPlatform().start(() -> {
      try {
        Thread.sleep(1_000);
        System.out.println("The winner is " + vote.vote("un"));
      } catch (InterruptedException e) {
        throw new AssertionError(e);
      }
    });
    System.out.println("The winner is " + vote.vote("zero"));
  }

Écrire la classe Vote et vérifier qu'elle passe les tests unitaires de VoteTest.java.

Vous pouvez utiliser une HashMap<String, Integer> pour stocker le nombre de votes qu'a reçus chaque String. Voici une méthode permettant de récupérer le gagnant à partir de la HashMap (on peut faire beaucoup plus joli en utlisant un Stream) :

  private String computeWinner() {
    var score = -1;
    String winner = null;
    for (var e : map.entrySet()) {
      var key = e.getKey();
      var value = e.getValue();
      if (value > score || (value == score && key.compareTo(winner) < 0)) {
        winner = key;
        score = value;
      }
    }
    return winner;
  }

Maximum Thread-Safe bloquant

Dans cet exercice, on va faire évoluer le programme de maximum thread-safe du TP précédent. Le thread main démarre 4 threads, chacun d'eux fait une boucle infinie qui, toutes les secondes :

Une fois que le thread main a démarré les quatres threads, on veut qu'il attende qu'un thread ait proposé une valeur supérieure à 8_000 et qu'il affiche "Un thread a tiré un valeur supérieure à 8 000" (sans afficher cette valeur pour l'instant). Une fois que c'est le cas, le thread main affichera le nombre maximum proposé par une thread toutes les secondes.

MAX_VALUE = 10_000 est une constante de la classe BlockingMaximum que vous allez écrire par la suite.

Le thread main et les 4 threads qu'il a démarré doivent communiquer au moyen d'un classe thread-safe BlockingMaximum. Donnez le contrat de cette classe, c'est à dire les méthodes publiques de la classe ainsi que le comportement attendu.

Écrire le code du main dans une classe Application ainsi que les méthodes de BlockingMaximum. Le programme va boucler à l'infini, c'est normal.

Thread[#22,Thread-2,5,main] a tiré 6296
Thread[#20,Thread-0,5,main] a tiré 1684
Thread[#21,Thread-1,5,main] a tiré 7227
Thread[#23,Thread-3,5,main] a tiré 30
Thread[#21,Thread-1,5,main] a tiré 8318
Thread[#20,Thread-0,5,main] a tiré 9208
Thread[#23,Thread-3,5,main] a tiré 6619
Thread[#22,Thread-2,5,main] a tiré 9653
Un thread a tiré un valeur supérieure à 8 000
Thread[#23,Thread-3,5,main] a tiré 5173
Thread[#22,Thread-2,5,main] a tiré 5777
Thread[#21,Thread-1,5,main] a tiré 5178
Thread[#20,Thread-0,5,main] a tiré 7996
Max courant : 9653
Thread[#22,Thread-2,5,main] a tiré 435
Thread[#21,Thread-1,5,main] a tiré 8350
Thread[#23,Thread-3,5,main] a tiré 909
Thread[#20,Thread-0,5,main] a tiré 1273
Max courant : 9653
...

(Optionnel) Modifier le code pour qu'au lieu d'afficher "Un thread a tiré un valeur supérieure à 8 000", le main affiche la première valeur supérieure à 8 000 proposée. Attention c'est plus subtile qu'il n'y paraît.