:: Enseignements :: ESIPE :: E4INFO :: 2019-2020 :: Collections Concurrentes ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
Modèle de mémoire, publication et Opérations atomiques
|
Problème de publication, Opérations atomiques, VarHandle et CAS
Exercice 1 - Publication safe
-
Expliquer ce qu'est le problème de publication ?
-
Le code suivant a un problème de publication, expliquer lequel.
public class Foo {
private String value;
public Foo(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Indiquer code avec un main suceptible de montrer le problème de publication.
-
Le code suivant a-t'il un problème de publication ? Si oui comment le corriger ?
public class Person {
private String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
...
}
-
Le code suivant a-t'il un problème de publication ? Si oui comment le corriger ?
public class Person {
private String name;
private volatile int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
...
}
-
Le code suivant a-t'il un problème de publication ? Si oui comment le corriger ?
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
new Thread(() -> {
System.out.println(this.name + " " + this.age);
}).start();
}
...
}
-
Le code suivant a-t'il un problème de publication ? Si oui comment le corriger ?
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
new Thread(() -> {
System.out.println(name + " " + age);
}).start();
}
...
}
Exercice 2 - Liste chainée avec ajout en fin
On souhaite écrire une implantation de liste chaînée de chaînes de caractères
avec insertion à la fin, concurrente et n'utilisant ni section critique, ni verrou.
La liste devra posséder deux méthodes
addLast(E e) et
size()
permettant respectivement d'ajouter un élément en fin de la liste et
de retourner la taille de la liste.
Le code ci-dessous est une implantation non-thread safe de la liste
Détail d'implantation: pour éviter de gérer différemment le cas de la liste vide,
une liste est créée initialement avec un faux maillon contenant la valeur
null.
L'idée de l'algorithme concurrent est la suivante:
pour éviter toute synchronisation lors de l'ajout,
on récupère le maillon à la fin de la liste puis on
teste en utilisant une opération atomique si le maillon est
toujours la fin de la liste; si oui, on lui assigne le nouveau
maillon créé. Si ce n'est pas le dernier maillon, on
récupère de nouveau le maillon à la fin de la liste et
on recommence la même opération...
-
Recopier le code de la classe StringList dans
une classe LockFreeStringList qui va implanter une liste
thread-safe sans verrou.
Dans un premier temps, implanter la méthode addLast.
Pour cela vous utiliserez la classe VarHandle.
Comment doit-on modifier le code de size pour
que la méthode soit thread-safe ?
-
On souhaite améliorer la complexité de la méthode addLast
en maintenant, en plus d'une référence sur la tête de la liste
(head), une référence sur la queue de la liste
(tail).
Modifier l'implantation de addLast pour utiliser
le champ tail et le mettre à jour.
Exercice 3 - Set 'Copy on Write'
On cherche à implanter un
Set (ensemble sans doublons) thread safe de façon
lock free.
Au lieu d'utiliser une liste chainée pour représenter les collisions (les éléments ayant le même index dans la table de hachage)
on se propose d'utiliser des petits tableaux contenant les collisions.
Par exemples, si l'on veut insérer les valeurs 7, 2, 7 et 15 dans une table de hachage à 8 éléments.
On départ on part avec le tableau de la table de hachage vide
0: [] 4: []
1: [] 5: []
2: [] 6: []
3: [] 7: []
Lorsque l'on insère 7 puis 3, on obtient
0: [] 4: []
1: [] 5: []
2: [2] 6: []
3: [] 7: [7]
Lorsque l'on cherche à ré-insérer 7, on ne fait rien car 7 est déjà présent, enfin lorsque l'on insère 15,
celui-ci est insérer à la suite de 7.
0: [] 4: []
1: [] 5: []
2: [2] 6: []
3: [] 7: [7, 15]
Normalement, une table de hachage doit se redimensionner si il y a trop d'éléments mais ici pour simplifier
la taille sera fixe et indiquée à la construction.
On se propose d'utiliser le code suivant
public class COWSet<E> {
private final E[][] hashArray;
private static final Object[] EMPTY = new Object[0];
@SuppressWarnings("unchecked")
public COWSet(int capacity) {
var array = new Object[capacity][];
Arrays.setAll(array, __ -> EMPTY);
this.hashArray = (E[][])array;
}
public boolean add(E element) {
Objects.requireNonNull(element);
var index = element.hashCode() % hashArray.length;
for (var e : hashArray[index]) {
if (element.equals(e)) {
return false;
}
}
var oldArray = hashArray[index];
var newArray = Arrays.copyOf(oldArray, oldArray.length + 1);
newArray[oldArray.length] = element;
hashArray[index] = newArray;
return true;
}
public void forEach(Consumer<? super E> consumer) {
for(var index = 0; index < hashArray.length; index++) {
var oldArray = hashArray[index];
for(var element: oldArray) {
consumer.accept(element);
}
}
}
}
-
Expliquer pourquoi le code ci-dessus n'est pas thread-safe ?
Vérifier en écrivant un main qui crée deux threads ajoutant chacune les nombres de 0 à 200 000 dans le set
puis vérifiant que chaque élément est bien présent une seule fois.
-
Modifier votre implantation pour la rendre thread-safe (et lock-free) utilisant la classe
VarHandle.
Attention à ce que la méthode forEach soit aussi thread safe !
© Université de Marne-la-Vallée