:: Enseignements :: ESIPE :: E4INFO :: 2019-2020 :: Collections Concurrentes ::
[LOGO]

Volatile, Opérations atomiques et CompareAndSet


Algorithme lock-free et opérations atomiques

Exercice 1 - A vos chronometres

On cherche à savoir combien d'itérations d'une boucle on peut faire en 100 millisecondes. Un de vos collègues a produit le code suivant:

  1. Sans exécuter le code, que fait ce programme ?
  2. Vérifier, en exécutant le programme (plusieurs fois), si vous avez vu juste.
  3. Comment doit-on corriger le problème ?
    Modifier la classe Bogus en conséquence.
  4. On cherche maintenant a accélérer le code de Bogus en utilisant le mot clé volatile au lieu des blocs synchronized.
    Créer une classe BogusVolatile qui n'utilise pas de bloc synchronized.
    Comment appelle-t-on les implantations qui n'ont ni blocs synchronized, ni lock ?

Exercice 2 - Compteur

On cherche à implanter un compteur thread-safe sous la forme d'une classe Counter et de sa méthode nextInt qui renvoie les valeurs du compteur.

   public class Counter {
     private int counter;
     
     public int nextInt() {
       return counter++;
     }
   }
  
  1. Expliquer pourquoi le code ci-dessus n'est pas thread-safe ?
    Ecrire un main démontrant le problème.
  2. Que se passe t'il si on déclare le champ counter volatile ?
  3. A quoi correspondt la valeur de retrour de la méthode compareAndSet de la classe AtomicInteger ?
    Utiliser la méthode compareAndSet pour créer une implantation thread-safe de la classe Counter.
  4. A quoi sert la méthode getAndIncrement de la classe AtomicInteger ?
    Créer une nouvelle classe Counter2, marchant comme Counter, utilisant la méthode getAndIncrement pour obtenir une implantation thread-safe.
  5. Que veut dire le terme lock-free pour un algorithme ?
    Les implantations Counter et Counter2 sont-elles lock-free ?

Exercice 3 - Liste chainée lock-free

On cherche à implanter une liste chainée par insertion en tête, et on utilise pour cela le code suivant

   public class Linked<E> {
     private static class Entry<E> {
       private final E element;
       private final Entry<E> next;
       
       private Entry(E element, Entry<E> next) {
         this.element = element;
         this.next = next;
       }
     }
   
     private Entry<E> head;
     
     public void addFirst(E element) {
       Objects.requireNonNull(element);
       head = new Entry<>(element, head);
     }
     
     public int size() {
       var size = 0;
       for(var link = head; link != null; link = link.next) {
         size ++;
       }
       return size;
     }
   }
  
  1. Expliquer pourquoi le code ci-dessus n'est pas thread-safe ?
    Ecrire un main démontrant le problème.
  2. Modifier votre implantation pour rendre la classe Linked thread-safe (et lock-free) utilisant la classe AtomicReference et sa méthode compareAndSet.
  3. Expliquer pourquoi utiliser la classe AtomicReference n'est pas super efficace ?
  4. Pour les plus balèzes, créer une classe Linked2 thread-safe (et lock-free) utilisant la classe VarHandle au lieu de la classe AtomicReference.
    Rappel: les types paramétrés sont érasés à l'exécution !