SlideShare une entreprise Scribd logo
1  sur  24
Télécharger pour lire hors ligne
Concurrence
Opérations atomiques
Rémi Forax
Opérations atomiques
La plupart des processeurs possède des
instructions assembleurs dites atomiques
– Effectuer un calcul en 1 seule opération
Par exemple, pour intel,
ladd rax 1 est équivalent à i++
Il est possible d'accéder en Java à ces
opérations grâce au package
java.util.concurrent.atomic
Atomique implique volatile
En Java, les opérations atomiques sont vues
comme des méthodes que l'on peut exécuter
sur un champ volatile
Deux façons de faire:
– la classe java.util.concurrent.AtomicInteger
● Encapsule un champ volatile (ici integer) et fournit des
méthodes atomiques
– la classe java.lang.invoke.VarHandle
● Référence un champ volatile d'une classe externe et
fournit des méthodes atomiques plus bas niveau
Exemple, un compteur thread-safe
Version avec synchronized:
public class ThreadSafeCounter {
private final Object lock = new Object();
private int counter;
public int nextValue() {
synchronized (lock) {
return counter++;
}
}
}
le code est thread-safe mais pas très rapide
Avec un AtomicInteger
Un AtomicInteger contient un champ volatile:
public class ThreadSafeCounter {
private final AtomicInteger counter =
new AtomicInteger();
public int nextValue() {
return counter.getAndIncrement();
}
}
getAndIncrement() est vu par le compilateur comme un
appel de méthode et comme une seule instruction
atomique par le JIT
Atomic* est une famille de classes
Il existe une classe spécifique pour chaque
type
– boolean -> AtomicBoolean
– int -> AtomicInteger
– long -> AtomicLong
– reference -> AtomicReference<T>
et aussi, pour les tableaux
– AtomicIntegerArray, AtomicLongArray,
AtomicReferenceArray<T>
Compare and Set
En fait, toutes les opérations atomiques ne sont
pas toutes disponibles sur tous les processeurs
mais il existe une primitive de base
Le compareAndSet ou CAS
Signature en C:
CAS(&field, expectedValue, newValue) -> boolean
Si le champ est == à expectedValue, alors la valeur
du champ devient newValue et on renvoie vrai
sinon on renvoie faux
Avec un AtomicInteger
AtomicInteger a une méthode compareAndSet:
public class ThreadSafeCounter {
private final AtomicInteger counter = new AtomicInteger();
public int nextValue() {
for(;;) {
int current = counter.get(); // volatile read
int newValue = current + 1;
if (counter.compareAndSet(current, newValue)) {
return current;
} // otherwise retry
}
}
}
Si le CAS marche, c'est équivalent à une écriture volatile
Attention, exemple de CAS
mal écrit !
public class BadCounter {
private final AtomicInteger counter =
new AtomicInteger();
public int nextValue() {
for(;;) {
if (counter.compareAndSet(counter.get(),
counter.get() + 1) {
return current;
}
}
}
}
On fait 2 appels à get() sans passer
par une variable temporaire
CAS + boucle avec une lambda
La méthode getAndUpdate(UnaryOperator) permet de
faire un CAS + boucle en indiquant uniquement la fonction
qui permet d’aller à la valeur suivante
public class ThreadSafeCounter {
private final AtomicInteger counter = new AtomicInteger();
public int nextValue() {
return counter.getAndUpdate(x -> x + 1);
}
}
Problème de AtomicInteger
AtomicInteger est simple à utiliser mais
– Il est gourmand en mémoire
il faut créer un autre objet pour chaque champ volatile
– Il nécessite une indirection en mémoire pour
chercher la valeur (donc un potentiel cache-miss)
La classe java.lang.invoke.VarHandle permet
de palier ces problèmes avec une API moins
simple
VarHandle
Un VarHandle correspond à ‘un pointeur’ (un handle) sur un
champ volatile ou une case d’un tableau (aussi volatile)
Lookup lookup = MethodHandles.lookup();
VarHandle handle = lookup.findVarHandle(
ThreadSafeCounter.class, // classe contenant le champ
"counter", // nom du champ
int.class); // type du champ
Lookup.findVarHandle() permet de créer un VarHandle à
partir d’un objet Lookup correspondant à un contexte de
sécurité.
MethodHandles.lookup() permet de demander le contexte de
sécurité à l’endroit de l’appel
Avec un VarHandle
Un compteur thread-safe avec un VarHandle:
public class ThreadSafeCounter {
private volatile int counter;
private final static VarHandle COUNTER_REF;
static {
Lookup lookup = MethodHandles.lookup();
try {
COUNTER_REF = lookup.findVarHandle(ThreadSafeCounter.class,
"counter", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
public int nextValue() {
...
}
}
Avec un VarHandle (2)
VarHandle possède une méthode compareAndSet:
public class ThreadSafeCounter {
private volatile int counter;
private final static VarHandle COUNTER_REF;
static {
COUNTER_REF = ...
}
public int nextValue() {
for(;;) {
int current = this.counter; // volatile read
if (COUNTER_REF.compareAndSet(this, current, current + 1)) {
return current;
} // otherwise retry
}
}
}
CAS ‘weak’ semantique
Un CAS ‘weak’ peut renvoyer false pour 2
raisons
– La valeur en RAM a été changée par une autre
thread
– Le coeur courant ne possède pas la cache line
contenant la valeur (car un autre la possède)
Le weak CAS est plus rapide sauf en cas de
forte contention
Avec un ‘weak’ CAS
VarHandle possède une méthode weakCompareAndSet:
public class ThreadSafeCounter {
private volatile int counter;
private final static VarHandle COUNTER_REF;
static {
COUNTER_REF = ...
}
public int nextValue() {
for(;;) {
int current = this.counter; // volatile read
if (COUNTER_REF.weakCompareAndSet(this, current, current + 1)) {
return current;
} // otherwise retry
}
}
}
Autres sémantiques
VarHandle possède d’autres méthodes
permettant d’avoir une gestion fine des effets
Fences:
– loadLoadFence, storeStoreFence, acquireFence,
releaseFence, fullFence
Autres instructions supportées par les CPUs
– getAndAdd, getAndBitwiseOr, getAndBitwiseAnd
Avec un getAndAdd
getAndAdd() prend en paramètre l’incrément
public class ThreadSafeCounter {
private volatile int counter;
private final static VarHandle COUNTER_REF;
static {
COUNTER_REF = ...
}
public int nextValue() {
return (int)COUNTER_REF.getAndAdd(this, 1);
}
}
Signature Polymorphique
Les méthodes de VarHandles sont ‘magiques’ dans le sens ou elles
acceptent différents paramètres sans faire de surcharge
Par contre, il faut faire un (faux) cast pour indiquer le type de retour
public class ThreadSafeCounter {
private volatile long counter;
private final static VarHandle COUNTER_REF = ...
.findVarHandle(ThreadSafeCounter.class, "counter", long.class);
public long nextValue() {
return (long)COUNTER_REF.getAndAdd(this, 1L);
}
}
VarHandle n’utilise pas les types paramétrés
Atomic*FieldUpdater
L’API des VarHandle a été introduit dans la
version 9, précédemment, on utilisait des
j.u.c.AtomicReferenceFieldUpdater
● Cette API est moins efficace car elle fait des
casts dynamiques
● ARFU.weakCompareAndSet a un gros bug,
l’écriture n’est pas volatile (aaaah)
Algorithmique concurrente
En fait, quasiment toutes les implantations des
structures de données présentes dans le
package java.util.concurrent n'utilisent pas de
bloc synchronized
On utilise
– soit des ReentrantLock
– soit pour les implantations “lock-free”
● Volatile et les opérations atomiques (souvent CAS)
Exemple
On veut une liste chainée concurrente lock-free avec insertion en tête
public class NotConcLinkedList {
static class Entry {
final Object value;
final Entry next;
Entry(Object value, Entry next) {
this.value = value;
this.next = next;
}
}
private Entry head;
public void add(Object value) {
this.head = new Entry(value, this.head);
}
...
}
Liste chainée Lock-free
public class LockFreeLinkedList {
static class Entry {
..
}
private final AtomicReference<Entry> reference =
new AtomicReference<>();
public void add(Object value) {
for(;;) {
Entry head = this.reference.get();
Entry entry = new Entry(value, head);
if (this.reference.compareAndSet(head, entry)) {
return;
}
}
}
}
Liste chainée Lock-free (2)
public class LockFreeLinkedList {
...
private final static VarHandle HEADER_REF;
static {
...
HEADER_REF = lookup.findVarHandle(LockFreeLinkedList.class,
"head", Entry.class);
…
}
private volatile Entry head;
public void add(Object value) {
for(;;) {
Entry head = this.head;
Entry entry = new Entry(value, head);
if (HEADER_REF.compareAndSet(this, head, entry)) {
return;
}
}
}
}

Contenu connexe

Similaire à 15-Concurrence-Operations-Atomiques.pdf

Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Normandy JUG
 
Design Pattern introduction
Design Pattern introductionDesign Pattern introduction
Design Pattern introductionneuros
 
Javascript ne se limite pas à jquery
Javascript ne se limite pas à jqueryJavascript ne se limite pas à jquery
Javascript ne se limite pas à jqueryneuros
 
Node.js, le pavé dans la mare
Node.js, le pavé dans la mareNode.js, le pavé dans la mare
Node.js, le pavé dans la mareValtech
 
Flutter : communication Android/iOS et les packages
Flutter : communication Android/iOS et les packagesFlutter : communication Android/iOS et les packages
Flutter : communication Android/iOS et les packagesEdouard Marquez
 
Découvrez C# 4.0 et les améliorations apportées à la BCL
Découvrez C# 4.0 et les améliorations apportées à la BCLDécouvrez C# 4.0 et les améliorations apportées à la BCL
Découvrez C# 4.0 et les améliorations apportées à la BCLDotNetHub
 
GWT : under the hood
GWT : under the hoodGWT : under the hood
GWT : under the hoodsvuillet
 
Javascript Json artchitecture
Javascript  Json artchitecture Javascript  Json artchitecture
Javascript Json artchitecture zaghir
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Dr Samir A. ROUABHI
 
16-Concurrence-APIs-Concurrentes.pdf
16-Concurrence-APIs-Concurrentes.pdf16-Concurrence-APIs-Concurrentes.pdf
16-Concurrence-APIs-Concurrentes.pdfPatiento Del Mar
 
Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Jean-Michel Doudoux
 
12-Concurrence-Rendez-vous.pdf
12-Concurrence-Rendez-vous.pdf12-Concurrence-Rendez-vous.pdf
12-Concurrence-Rendez-vous.pdfPatiento Del Mar
 
6- Javacousesforenginerss_reseaux_v2.pdf
6- Javacousesforenginerss_reseaux_v2.pdf6- Javacousesforenginerss_reseaux_v2.pdf
6- Javacousesforenginerss_reseaux_v2.pdfAliouDiallo24
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScriptMicrosoft
 

Similaire à 15-Concurrence-Operations-Atomiques.pdf (20)

Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) f...
 
Design Pattern introduction
Design Pattern introductionDesign Pattern introduction
Design Pattern introduction
 
Javascript ne se limite pas à jquery
Javascript ne se limite pas à jqueryJavascript ne se limite pas à jquery
Javascript ne se limite pas à jquery
 
Node.js, le pavé dans la mare
Node.js, le pavé dans la mareNode.js, le pavé dans la mare
Node.js, le pavé dans la mare
 
Flutter : communication Android/iOS et les packages
Flutter : communication Android/iOS et les packagesFlutter : communication Android/iOS et les packages
Flutter : communication Android/iOS et les packages
 
Découvrez C# 4.0 et les améliorations apportées à la BCL
Découvrez C# 4.0 et les améliorations apportées à la BCLDécouvrez C# 4.0 et les améliorations apportées à la BCL
Découvrez C# 4.0 et les améliorations apportées à la BCL
 
GWT : under the hood
GWT : under the hoodGWT : under the hood
GWT : under the hood
 
JAVA Chapitre7
JAVA Chapitre7JAVA Chapitre7
JAVA Chapitre7
 
Javascript Json artchitecture
Javascript  Json artchitecture Javascript  Json artchitecture
Javascript Json artchitecture
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)
 
Vert.x 3
Vert.x 3Vert.x 3
Vert.x 3
 
Cours java script
Cours java scriptCours java script
Cours java script
 
16-Concurrence-APIs-Concurrentes.pdf
16-Concurrence-APIs-Concurrentes.pdf16-Concurrence-APIs-Concurrentes.pdf
16-Concurrence-APIs-Concurrentes.pdf
 
JAVA
JAVAJAVA
JAVA
 
Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016Retours sur java 8 devoxx fr 2016
Retours sur java 8 devoxx fr 2016
 
12-Concurrence-Rendez-vous.pdf
12-Concurrence-Rendez-vous.pdf12-Concurrence-Rendez-vous.pdf
12-Concurrence-Rendez-vous.pdf
 
TP2 RMI
TP2 RMITP2 RMI
TP2 RMI
 
6- Javacousesforenginerss_reseaux_v2.pdf
6- Javacousesforenginerss_reseaux_v2.pdf6- Javacousesforenginerss_reseaux_v2.pdf
6- Javacousesforenginerss_reseaux_v2.pdf
 
Introduction à JavaScript
Introduction à JavaScriptIntroduction à JavaScript
Introduction à JavaScript
 
module_I6_Sockets.pdf
module_I6_Sockets.pdfmodule_I6_Sockets.pdf
module_I6_Sockets.pdf
 

Plus de Patiento Del Mar

Plus de Patiento Del Mar (19)

Managed Extenibility Framework in C# - MEF
Managed Extenibility Framework  in C#  - MEFManaged Extenibility Framework  in C#  - MEF
Managed Extenibility Framework in C# - MEF
 
grpc_tutorial.pdf
grpc_tutorial.pdfgrpc_tutorial.pdf
grpc_tutorial.pdf
 
0.coursGitEclipse.pdf
0.coursGitEclipse.pdf0.coursGitEclipse.pdf
0.coursGitEclipse.pdf
 
Primefaces.ppt
Primefaces.pptPrimefaces.ppt
Primefaces.ppt
 
hibernateormoverview-140501154227-phpapp02.pdf
hibernateormoverview-140501154227-phpapp02.pdfhibernateormoverview-140501154227-phpapp02.pdf
hibernateormoverview-140501154227-phpapp02.pdf
 
hibernateormfeatures-140223193044-phpapp02.pdf
hibernateormfeatures-140223193044-phpapp02.pdfhibernateormfeatures-140223193044-phpapp02.pdf
hibernateormfeatures-140223193044-phpapp02.pdf
 
Spring Data Advanced Querying.ppt
Spring Data Advanced Querying.pptSpring Data Advanced Querying.ppt
Spring Data Advanced Querying.ppt
 
Thymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.pptThymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.ppt
 
Spring Security.ppt
Spring Security.pptSpring Security.ppt
Spring Security.ppt
 
momjms.pdf
momjms.pdfmomjms.pdf
momjms.pdf
 
rmi.pdf
rmi.pdfrmi.pdf
rmi.pdf
 
synchronization.pdf
synchronization.pdfsynchronization.pdf
synchronization.pdf
 
cours6.pdf
cours6.pdfcours6.pdf
cours6.pdf
 
java_PAR.pdf
java_PAR.pdfjava_PAR.pdf
java_PAR.pdf
 
17-Concurrence-Fork-Join.pdf
17-Concurrence-Fork-Join.pdf17-Concurrence-Fork-Join.pdf
17-Concurrence-Fork-Join.pdf
 
13-Concurrence-Producteur-consommateur.pdf
13-Concurrence-Producteur-consommateur.pdf13-Concurrence-Producteur-consommateur.pdf
13-Concurrence-Producteur-consommateur.pdf
 
Concurrence-Interruption-Exceptions.pdf
Concurrence-Interruption-Exceptions.pdfConcurrence-Interruption-Exceptions.pdf
Concurrence-Interruption-Exceptions.pdf
 
threads.pdf
threads.pdfthreads.pdf
threads.pdf
 
Chap7_JavaNet.pdf
Chap7_JavaNet.pdfChap7_JavaNet.pdf
Chap7_JavaNet.pdf
 

15-Concurrence-Operations-Atomiques.pdf

  • 2. Opérations atomiques La plupart des processeurs possède des instructions assembleurs dites atomiques – Effectuer un calcul en 1 seule opération Par exemple, pour intel, ladd rax 1 est équivalent à i++ Il est possible d'accéder en Java à ces opérations grâce au package java.util.concurrent.atomic
  • 3. Atomique implique volatile En Java, les opérations atomiques sont vues comme des méthodes que l'on peut exécuter sur un champ volatile Deux façons de faire: – la classe java.util.concurrent.AtomicInteger ● Encapsule un champ volatile (ici integer) et fournit des méthodes atomiques – la classe java.lang.invoke.VarHandle ● Référence un champ volatile d'une classe externe et fournit des méthodes atomiques plus bas niveau
  • 4. Exemple, un compteur thread-safe Version avec synchronized: public class ThreadSafeCounter { private final Object lock = new Object(); private int counter; public int nextValue() { synchronized (lock) { return counter++; } } } le code est thread-safe mais pas très rapide
  • 5. Avec un AtomicInteger Un AtomicInteger contient un champ volatile: public class ThreadSafeCounter { private final AtomicInteger counter = new AtomicInteger(); public int nextValue() { return counter.getAndIncrement(); } } getAndIncrement() est vu par le compilateur comme un appel de méthode et comme une seule instruction atomique par le JIT
  • 6. Atomic* est une famille de classes Il existe une classe spécifique pour chaque type – boolean -> AtomicBoolean – int -> AtomicInteger – long -> AtomicLong – reference -> AtomicReference<T> et aussi, pour les tableaux – AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray<T>
  • 7. Compare and Set En fait, toutes les opérations atomiques ne sont pas toutes disponibles sur tous les processeurs mais il existe une primitive de base Le compareAndSet ou CAS Signature en C: CAS(&field, expectedValue, newValue) -> boolean Si le champ est == à expectedValue, alors la valeur du champ devient newValue et on renvoie vrai sinon on renvoie faux
  • 8. Avec un AtomicInteger AtomicInteger a une méthode compareAndSet: public class ThreadSafeCounter { private final AtomicInteger counter = new AtomicInteger(); public int nextValue() { for(;;) { int current = counter.get(); // volatile read int newValue = current + 1; if (counter.compareAndSet(current, newValue)) { return current; } // otherwise retry } } } Si le CAS marche, c'est équivalent à une écriture volatile
  • 9. Attention, exemple de CAS mal écrit ! public class BadCounter { private final AtomicInteger counter = new AtomicInteger(); public int nextValue() { for(;;) { if (counter.compareAndSet(counter.get(), counter.get() + 1) { return current; } } } } On fait 2 appels à get() sans passer par une variable temporaire
  • 10. CAS + boucle avec une lambda La méthode getAndUpdate(UnaryOperator) permet de faire un CAS + boucle en indiquant uniquement la fonction qui permet d’aller à la valeur suivante public class ThreadSafeCounter { private final AtomicInteger counter = new AtomicInteger(); public int nextValue() { return counter.getAndUpdate(x -> x + 1); } }
  • 11. Problème de AtomicInteger AtomicInteger est simple à utiliser mais – Il est gourmand en mémoire il faut créer un autre objet pour chaque champ volatile – Il nécessite une indirection en mémoire pour chercher la valeur (donc un potentiel cache-miss) La classe java.lang.invoke.VarHandle permet de palier ces problèmes avec une API moins simple
  • 12. VarHandle Un VarHandle correspond à ‘un pointeur’ (un handle) sur un champ volatile ou une case d’un tableau (aussi volatile) Lookup lookup = MethodHandles.lookup(); VarHandle handle = lookup.findVarHandle( ThreadSafeCounter.class, // classe contenant le champ "counter", // nom du champ int.class); // type du champ Lookup.findVarHandle() permet de créer un VarHandle à partir d’un objet Lookup correspondant à un contexte de sécurité. MethodHandles.lookup() permet de demander le contexte de sécurité à l’endroit de l’appel
  • 13. Avec un VarHandle Un compteur thread-safe avec un VarHandle: public class ThreadSafeCounter { private volatile int counter; private final static VarHandle COUNTER_REF; static { Lookup lookup = MethodHandles.lookup(); try { COUNTER_REF = lookup.findVarHandle(ThreadSafeCounter.class, "counter", int.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new AssertionError(e); } } public int nextValue() { ... } }
  • 14. Avec un VarHandle (2) VarHandle possède une méthode compareAndSet: public class ThreadSafeCounter { private volatile int counter; private final static VarHandle COUNTER_REF; static { COUNTER_REF = ... } public int nextValue() { for(;;) { int current = this.counter; // volatile read if (COUNTER_REF.compareAndSet(this, current, current + 1)) { return current; } // otherwise retry } } }
  • 15. CAS ‘weak’ semantique Un CAS ‘weak’ peut renvoyer false pour 2 raisons – La valeur en RAM a été changée par une autre thread – Le coeur courant ne possède pas la cache line contenant la valeur (car un autre la possède) Le weak CAS est plus rapide sauf en cas de forte contention
  • 16. Avec un ‘weak’ CAS VarHandle possède une méthode weakCompareAndSet: public class ThreadSafeCounter { private volatile int counter; private final static VarHandle COUNTER_REF; static { COUNTER_REF = ... } public int nextValue() { for(;;) { int current = this.counter; // volatile read if (COUNTER_REF.weakCompareAndSet(this, current, current + 1)) { return current; } // otherwise retry } } }
  • 17. Autres sémantiques VarHandle possède d’autres méthodes permettant d’avoir une gestion fine des effets Fences: – loadLoadFence, storeStoreFence, acquireFence, releaseFence, fullFence Autres instructions supportées par les CPUs – getAndAdd, getAndBitwiseOr, getAndBitwiseAnd
  • 18. Avec un getAndAdd getAndAdd() prend en paramètre l’incrément public class ThreadSafeCounter { private volatile int counter; private final static VarHandle COUNTER_REF; static { COUNTER_REF = ... } public int nextValue() { return (int)COUNTER_REF.getAndAdd(this, 1); } }
  • 19. Signature Polymorphique Les méthodes de VarHandles sont ‘magiques’ dans le sens ou elles acceptent différents paramètres sans faire de surcharge Par contre, il faut faire un (faux) cast pour indiquer le type de retour public class ThreadSafeCounter { private volatile long counter; private final static VarHandle COUNTER_REF = ... .findVarHandle(ThreadSafeCounter.class, "counter", long.class); public long nextValue() { return (long)COUNTER_REF.getAndAdd(this, 1L); } } VarHandle n’utilise pas les types paramétrés
  • 20. Atomic*FieldUpdater L’API des VarHandle a été introduit dans la version 9, précédemment, on utilisait des j.u.c.AtomicReferenceFieldUpdater ● Cette API est moins efficace car elle fait des casts dynamiques ● ARFU.weakCompareAndSet a un gros bug, l’écriture n’est pas volatile (aaaah)
  • 21. Algorithmique concurrente En fait, quasiment toutes les implantations des structures de données présentes dans le package java.util.concurrent n'utilisent pas de bloc synchronized On utilise – soit des ReentrantLock – soit pour les implantations “lock-free” ● Volatile et les opérations atomiques (souvent CAS)
  • 22. Exemple On veut une liste chainée concurrente lock-free avec insertion en tête public class NotConcLinkedList { static class Entry { final Object value; final Entry next; Entry(Object value, Entry next) { this.value = value; this.next = next; } } private Entry head; public void add(Object value) { this.head = new Entry(value, this.head); } ... }
  • 23. Liste chainée Lock-free public class LockFreeLinkedList { static class Entry { .. } private final AtomicReference<Entry> reference = new AtomicReference<>(); public void add(Object value) { for(;;) { Entry head = this.reference.get(); Entry entry = new Entry(value, head); if (this.reference.compareAndSet(head, entry)) { return; } } } }
  • 24. Liste chainée Lock-free (2) public class LockFreeLinkedList { ... private final static VarHandle HEADER_REF; static { ... HEADER_REF = lookup.findVarHandle(LockFreeLinkedList.class, "head", Entry.class); … } private volatile Entry head; public void add(Object value) { for(;;) { Entry head = this.head; Entry entry = new Entry(value, head); if (HEADER_REF.compareAndSet(this, head, entry)) { return; } } } }