Accès aux variables partagées, atomicité
Exercice 1 - Modification d'une variable en concurrence
On souhaite créer deux threads qui change le même un champs
d'un même objet :
public class Test {
int value;
public static void main(String[] args) {
final Test test=new Test();
for(int i=0;i<2;i++) {
final int id=i;
new Thread(new Runnable() {
public void run() {
for(;;) {
test.value=id;
if (test.value!=id)
System.out.println("id "+id+" "+test.value);
}
}
}).start();
}
}
}
-
Qu'affiche le code suivant avec la ligne de commande :
java Test
Qu'affiche le même programme mais avec la commande
java -server Test
Expliquer.
-
Comment doit on faire pour être sûr que chaque thread voit
les modifications effectués sur une variable par l'autre thread.
Exercice 2 - strtok
On souhaite écrire une méthode strtok qui :
-
lorsqu'elle est appelée avec un chaîne de caractère
(CharSequence) et un délimiteur (char),
renvoie un CharSequence correspondant
à la chaine du début de la chaine jusqu'à la prochaine
occurence du délimiteur.
-
lorsqu'elle est appelée avec null et un délimiteur,
renvoie un CharSequence de la position lors du dernier appel
à la méthode jusqu'à la prochaine occurence du délimiteur.
Exemple :
CharSequence seq1=strtok("toto est beau",' '); // toto
CharSequence seq2=strtok(null,' '); // est
CharSequence seq2=strtok(null,' '); // beau
CharSequence seq2=strtok(null,' '); // null
Faire en sorte que l'on puisse exécuter cette méthode
depuis plusieurs threads différentes en même temps.
Penser aux variables locales à un thread.
On rappel qu'il existe une méthode subSequence()
qui permet d'extraire une sous-chaîne d'une chaîne existante.
Exercice 3 - Liste concurrente et atomicité
On souhaite écrire une implantation de liste chaînée concurrente
n'utilisant ni section critique ni verrou.
La liste devra possèder deux méthodes addLast(E e)
et toString() permettant respectivement
d'ajouter un élement en queue de liste et d'afficher la liste.
Lors de la création, la liste sera créée avec un maillon.
L'idée pour éviter toutes synchronizations lors de
l'ajout consiste à récuperer le maillon à la fin de la liste
puis à tester en une opération atomique si le maillon
est toujours la fin de la liste et si oui à lui assigner
le nouveau maillon créé. Si ce n'est pas le dernier maillon,
on récupère de nouveau la fin de la liste
et l'on recommence le même algorithme.
Dans un premier temps, nous allons définir la liste comme contenant
juste un élement ainsi qu'une référence sur un autre élement de la liste.
L'ajout à la fin de la liste se fera par un parcours du chaînage.
-
Définir la classe ConcurrentLink avec ces champs element
et next ainsi que son constructeur
ConcurrentLink(E firstElement).
-
Expliquer pourquoi le champ element doit être déclaré
final ?
-
Implanter la méthode addLast en utilisant pour
tester et assigner en une opération atomique,
la classe AtomicReferenceFieldUpdater
du paquetage java.util.concurrent.atomic.
-
Implanter la méthode toString.
On souhaite maintenant séparer l'implantation de la liste
de celle d'un maillon dans le but de stocker le dernier maillon
pour effectuer un ajout plus efficace.
-
Définir la classe ConcurrentList
pour quel contiennent deux champs head et tail
correspondant respectivement au début et à la fin de la liste
ainsi que la classe interne Entry correspondant
à un maillon de la liste chaînée.
Pour simplifier les choses, la liste ne pourra être vide,
elle sera donc construite avec un élement.
-
Implanter la méthode addLast
Attention, il y a un petit piège lorsque l'on met à jour
la référence correspondant à la fin de la liste.
-
Implanter la méthode toString.
Si vous avez réussi bravo, vous venez de ré-implanter une partie
de la classe ConcurrentLinkedQueue.
Rémi Forax - Université de Marne La Vallée