:: Enseignements :: ESIPE :: E4INFO :: 2015-2016 :: Java Réseau I - Concurrence et E/S ::
[LOGO]

Examen de concurrence sur table


Vous n'avez droit à aucun document.

Exercice 1 - Questions de cours (7)

Répondez aux questions suivantes en deux ou trois phrases, pas plus, et SVP répondez à la question pas au mot-clef.

  1. Qu'est ce qu'une classe thread-safe ?
  2. Sachant que le code de la méthode add de java.util.ArrayList est le suivant,
          public class ArrayList<E> ... { 
            transient Object[] elementData;
            private int size;
            
            public boolean add(E e) {
              ...
              elementData[size++] = e;
              return true;
            }
          }
          
    expliquer pourquoi java.util.ArrayList n'est pas thread-safe.
  3. Pourquoi dit-on que déclarer une méthode synchronized n'est pas une bonne idée. Quel est le problème ?
  4. Que fait Thread.currentThread().interrupt() ?
  5. Quel est le problème dans la méthode pop dans le code suivant ?
          class IntStack {
            private final int[] array = new int[16];
            private int top;
          
            public int pop() throws InterruptedException {
              synchronized(array) {
                if (top == 0) {
                  array.wait();
                }
                return array[--top];
              }
            }
            ...
          
    Que faut-il modifier pour corriger ce problème?
  6. Y-a-t'il un problème de publication dans le code ci-dessous ?
    Quels sont les différents affichages possibles ?
            class Test {
              final int value;
              
              Test(int val) {
                this.value = val;
                new Thread(() -> {
                  System.out.println(value);
                }).start();
              }
            
              public static void main(String[] args) {
                Test test = new Test(3);
                System.out.println("end");
              }
            }
          
  7. Y-a-t'il un problème de publication dans le code ci-dessous ?
    Quels sont les différents affichages possibles ?
            class Test {
              int value;
              
              void test() {
                new Thread(() -> {
                  System.out.println(value);
                }).start();
              }
            
              public static void main(String[] args) {
                Test test = new Test();
                test.value = 3;
                test.test();
                System.out.println("end");
              }
            }
          

Exercice 2 - Mon lock à moi (7)

On cherche écrire un lock comme java.util.concurrent.lock.ReentrantLock, avec deux méthodes lock et unlock, mais qui contrairement à ReentrantLock sera non-reentrant.
On utilisera pour cela les opérations atomiques.

  1. Rappeler ce que ré-entrant veut dire.
  2. En utilisant des opérations atomiques, on cherche à écrire une classe MyLock possédant deux méthodes lock et unlock et implantant un verrou non-réentrant. L'idée est la suivante: pour savoir si une thread possède le jeton, on va utiliser une case mémoire; si la valeur de la case mémoire est 1, alors le jeton n'est pas pris, si la valeur de la case mémoire est 0, alors le jeton est pris.
    Si l'on veut acquérir le verrou et que le jeton est déjà pris, pour simuler le fait que la thread courante doit se mettre en attente, on utilisera Thread.sleep (rappel, Thread.sleep lève l'exception InterruptedException); c'est mal, mais c'est ce que demande l'exercice.
    Pour des questions de performance, doit-on utiliser plutôt la classe AtomicInteger ou AtomicIntegerFieldUpdater lors de l'implantation de la classe MyLock?
    Indiquer le code de la classe MyLock et de ses deux méthodes lock et unlock.
  3. Que se passe-t-il si une thread est en attente sur la méthode lock de la classe ReentrantLock et que celle-ci est interrompue ?
    Votre implantation fait elle la même chose ?
  4. Que se passe-t-il en terme d'effet en mémoire lorsque l'on appelle la méthode lock sur un ReentrantLock ?
    Votre implantation offre-t-elle les mêmes garanties ?
    Même question avec la méthode unlock.
  5. Que se passe-t-il si on supprime l'appel à Thread.sleep ?
  6. Fournir un code pour la méthode unlock de sorte que si on appelle unlock sans avoir appelé lock, la méthode unlock lève l'exception IllegalMonitorStateException (qui hérite de RuntimeException).

Exercice 3 - La piscine est le future (6)

On souhaite ré-implanter le mécanisme de pool de thread (d'executeur) et de future qui est déjà présent dans le package java.util.concurrent, mais sans utiliser de classes de ce package ni de ses sous-packages.
Par exemple, si l'on veut exécuter 10 fois System.nanoTime() avec un pool de 4 threads, et récupérer les résultats sous forme de future on utilisera le code suivant.
  public static void main(String[] args) throws InterruptedException {
    MyThreadPool threadPool = MyThreadPool.createThreadPool(4);
    ArrayList<MyFuture<Long>> list = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
      list.add(threadPool.submit(() -> System.nanoTime()));
    }
    for(MyFuture<Long> future: list) {
      System.out.println(future.get());
    }
  }
   
Un stagiaire propose l'implantation ci-dessous:
  @FunctionalInterface
  public interface MyCallable<V> {
    public V call();
  }
  
  public class MyThreadPool {
    private final ArrayDeque<Future<?>> queue;
    
    private MyThreadPool(ArrayDeque<Future<?>> queue) {
      this.queue = queue;
    }
    
    public <V> MyFuture<V> submit(MyCallable<? extends V> callable) {
      Objects.requireNonNull(callable);
      MyFuture<V> future = new MyFuture<>(callable);
      queue.offer(future);
      return future;
    }
    
    public static MyThreadPool createThreadPool(int threadCount) {
      ArrayDeque<MyFuture<?>> queue = new ArrayDeque<>();
      MyThreadPool pool = new MyThreadPool(queue);
      for(int i = 0; i < threadCount; i++) {
        Thread t = new Thread(() -> {
          for(;;) {
            MyFuture<?> future = queue.poll();
            if (future != null) {
              future.call();
            }
          }
        });
        t.start();
      }
      return pool;
    }
  }
  
  public class MyFuture<V> {
    private final MyCallable<? extends V> callable;
    private V value;
    
    /* package accessibility */
    MyFuture(MyCallable<? extends V> callable) {
      this.callable = callable;
    }
    
    /* package accessibility */
    void call() {
      value = callable.call();
    }
    
    public V get() {
      return value;
    }
  }
   

  1. On s'intéresse dans un premier temps à la classe MyThreadPool.
    On peut remarquer qu'elle n'est pas thread-safe et quelle fait une attente active.
    Indiquer un nouveau code pour la classe MyThreadPool qui corrige ces deux problèmes (l'idée est toujours de ne pas utiliser de classes du package java.util.concurrent et ses sous-packages).
  2. On s'intérresse maintenant à la classe MyFuture: quels sont les deux problèmes du code de cette classe?
    Proposer un nouveau code qui corrige ces deux problèmes.