AtomicInteger lazySet vs set

116

Quelle est la différence entre les méthodes lazySetet setde AtomicInteger? La documentation n'a pas grand-chose à dire sur lazySet:

Définit finalement la valeur donnée.

Il semble que la valeur stockée ne sera pas immédiatement définie sur la valeur souhaitée, mais qu'elle sera plutôt programmée pour être définie dans le futur. Mais, quelle est l'utilisation pratique de cette méthode? Un exemple?

Cheok Yan Cheng
la source

Réponses:

114

Cité directement à partir de "JDK-6275329: Ajouter des méthodes lazySet aux classes atomiques" :

Comme probablement le dernier petit suivi JSR166 de Mustang, nous avons ajouté une méthode "lazySet" aux classes Atomic (AtomicInteger, AtomicReference, etc.). Il s'agit d'une méthode de niche qui est parfois utile lors du réglage fin du code à l'aide de structures de données non bloquantes. La sémantique est que l'écriture est garantie de ne pas être réordonnée avec une écriture précédente, mais peut être réorganisée avec des opérations ultérieures (ou de manière équivalente, peut ne pas être visible pour les autres threads) jusqu'à ce qu'une autre opération d'écriture volatile ou de synchronisation se produise).

Le cas d'utilisation principal est d'annuler les champs de nœuds dans des structures de données non bloquantes uniquement dans le but d'éviter la rétention des déchets à long terme; il s'applique lorsqu'il est inoffensif si d'autres threads voient des valeurs non nulles pendant un certain temps, mais vous souhaitez vous assurer que les structures sont finalement GCable. Dans de tels cas, vous pouvez obtenir de meilleures performances en évitant les coûts de l'écriture volatile nulle. Il existe également quelques autres cas d'utilisation dans ce sens pour les atomiques non basés sur des références, de sorte que la méthode est prise en charge dans toutes les classes AtomicX.

Pour les personnes qui aiment penser à ces opérations en termes de barrières au niveau de la machine sur des multiprocesseurs courants, lazySet fournit une barrière magasin-magasin précédente (qui est soit une barrière sans opération soit très bon marché sur les plates-formes actuelles), mais pas de barrière de charge de stockage. (qui est généralement la partie coûteuse d'une écriture volatile).

bâillement
la source
14
Quelqu'un pourrait-il abattre pour le reste d'entre nous? :(
Gaurav
14
Lazy est la version non volatile (par exemple, le changement d'état n'est pas garanti d'être visible pour tous les threads qui ont la Atomic*portée).
bâillement
63
ce que je ne comprends pas, c'est pourquoi javadoc est si pauvre.
Felipe
8
Je suis sûr qu'ils finiront par le changer. Boom boom.
MMJZ
3
pour ceux qui veulent en savoir plus sur la barrière magasin / chargement et pourquoi la barrière magasin-magasin est moins chère que la barrière de charge magasin. Voici un article facile à comprendre à ce sujet. Mechanical-sympathy.blogspot.com/2011/07/…
Kin Cheung
15

lazySet peut être utilisé pour la communication inter thread rmw, car xchg est atomique, comme pour la visibilité, lorsque le processus de thread d'écriture modifie un emplacement de ligne de cache, le processeur du thread de lecture le verra à la lecture suivante, car le protocole de cohérence du cache du processeur Intel garantira LazySet fonctionne, mais la ligne de cache sera mise à jour à la prochaine lecture, encore une fois, le processeur doit être suffisamment moderne.

http://sc.tamu.edu/systems/eos/nehalem.pdf Pour Nehalem qui est une plate-forme multiprocesseur, les processeurs ont la capacité de «fouiner» (espionner) le bus d'adresses pour les accès d'autres processeurs à la mémoire système et dans leurs caches internes. Ils utilisent cette capacité de surveillance pour garder leurs caches internes cohérents à la fois avec la mémoire système et avec les caches dans d'autres processeurs interconnectés. Si, grâce à la surveillance, un processeur détecte qu'un autre processeur a l'intention d'écrire dans un emplacement de mémoire qu'il a actuellement mis en cache dans l'état partagé, le processeur de surveillance invalidera son bloc de cache, le forçant à effectuer un remplissage de ligne de cache la prochaine fois qu'il accédera au même emplacement de mémoire .

oracle hotspot jdk pour architecture cpu x86->

lazySet == unsafe.putOrderedLong == xchg rw (instruction asm qui sert de barrière souple coûtant 20 cycles sur le processeur nehelem Intel)

sur x86 (x86_64) une telle barrière est beaucoup moins chère en termes de performances que volatile ou AtomicLong getAndAdd,

Dans un scénario de file d'attente à un producteur et un consommateur, la barrière souple xchg peut forcer la ligne de codes avant le lazySet (séquence + 1) pour que le thread producteur se produise AVANT tout code de thread consommateur qui consommera (travaillera sur) les nouvelles données, bien sûr Le thread consommateur devra vérifier de manière atomique que la séquence du producteur a été incrémentée d'exactement un en utilisant un compareAndSet (sequence, sequence + 1).

J'ai suivi le code source de Hotspot pour trouver le mappage exact du lazySet au code cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> Définition SET_FIELD_VOLATILE -> OrderAccess: release_store_fence. Pour x86_64, OrderAccess: release_store_fence est défini comme utilisant l'instruction xchg.

Vous pouvez voir comment il est exactement défini dans jdk7 (doug lea travaille sur de nouveaux éléments pour JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

vous pouvez également utiliser hdis pour désassembler l'assembly du code lazySet en action.

Il y a une autre question connexe: avons-nous besoin de mfence lors de l'utilisation de xchg

côtelette de porc
la source
5
Il est difficile de comprendre ce que vous voulez en venir ici. Pouvez-vous clarifier votre point de vue?
Paul Bellora
3
"lazySet == unsafe.putOrderedLong == xchg rw (instruction asm qui sert de barrière souple coûtant 20 cycles sur le processeur nehelem Intel) sur x86 (x86_64) une telle barrière est beaucoup moins chère en termes de performances que volatile ou AtomicLong getAndAdd" -> Ce n'est pas vrai à ma connaissance. lazySet / putOrdered est un MOV vers une adresse, c'est pourquoi le livre de recettes JMM le décrit comme un no-op sur x86.
Nitsan Wakart
11

Une discussion plus large sur les origines et l'utilité de lazySet et du putOrdered sous-jacent peut être trouvée ici: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Pour résumer: lazySet est une écriture volatile faible dans le sens où il agit comme un magasin de stockage et non comme une clôture de stockage. Cela se résume à lazySet étant compilé JIT en une instruction MOV qui ne peut pas être réordonnée par le compilateur plutôt que l'instruction nettement plus coûteuse utilisée pour un ensemble volatile.

Lors de la lecture de la valeur, vous finissez toujours par faire une lecture volatile (avec un Atomic * .get () dans tous les cas).

lazySet offre à un seul écrivain un mécanisme d'écriture volatile cohérent, c'est-à-dire qu'il est parfaitement légitime pour un seul écrivain d'utiliser lazySet pour incrémenter un compteur, plusieurs threads incrémentant le même compteur devront résoudre les écritures concurrentes en utilisant CAS, ce qui se passe exactement sous les jaquettes d'Atomic * pour incAndGet.

Nitsan Wakart
la source
exactement, pourquoi ne pouvons-nous pas dire que c'est une simple StoreStorebarrière, mais pas une StoreLoad?
Eugene
8

À partir du résumé du package Concurrent-atomic

lazySet a les effets de mémoire d'écrire (assigner) une variable volatile sauf qu'il permet des réorganisations avec des actions mémoire ultérieures (mais pas précédentes) qui n'imposent pas elles-mêmes des contraintes de réorganisation avec les écritures non volatiles ordinaires. Entre autres contextes d'utilisation, lazySet peut s'appliquer lors de l'annulation, dans un souci de garbage collection, une référence qui n'est plus jamais accédée.

Si vous êtes curieux de lazySet, vous vous devez également d'autres explications.

Les effets de mémoire pour les accès et les mises à jour des atomiques suivent généralement les règles pour les volatiles, comme indiqué dans la section 17.4 de la spécification du langage Java ™.

get a les effets mémoire de la lecture d'une variable volatile.

set a les effets de mémoire d'écrire (d'affecter) une variable volatile.

lazySet a les effets de mémoire d'écrire (assigner) une variable volatile sauf qu'il permet des réorganisations avec des actions mémoire ultérieures (mais pas précédentes) qui n'imposent pas elles-mêmes des contraintes de réorganisation avec les écritures non volatiles ordinaires. Entre autres contextes d'utilisation, lazySet peut s'appliquer lors de l'annulation, dans un souci de garbage collection, une référence qui n'est plus jamais accédée.

lowCompareAndSet lit et écrit de manière atomique une variable, mais ne crée aucun ordre d' occurrence avant, donc ne fournit aucune garantie en ce qui concerne les lectures et écritures précédentes ou suivantes de toutes les variables autres que la cible de lowCompareAndSet.

compareAndSet et toutes les autres opérations de lecture et de mise à jour telles que getAndIncrement ont les effets mémoire de la lecture et de l'écriture de variables volatiles.

Ajeet Ganga
la source
4

Voici ma compréhension, corrigez-moi si je me trompe: vous pouvez penser à lazySet()"semi" volatile: c'est fondamentalement une variable non volatile en termes de lecture par d'autres threads, c'est-à-dire que la valeur définie par lazySet peut ne pas être visible pour les autres fils. Mais il devient volatil lorsqu'une autre opération d'écriture se produit (peut être à partir d'autres threads). Le seul impact de lazySet que je peux imaginer est compareAndSet. Donc, si vous utilisez lazySet(), get()d'autres threads peuvent toujours obtenir l'ancienne valeur, mais compareAndSet()auront toujours la nouvelle valeur car il s'agit d'une opération d'écriture.

jyluo
la source
1
tu ne veux pas dire compareAndSet?
Dave Moten
2

Re: essayez de le baisser -

Vous pouvez considérer cela comme un moyen de traiter un champ volatil comme s'il n'était pas volatil pour une opération de magasin particulière (par exemple: ref = null;).

Ce n'est pas tout à fait exact, mais cela devrait suffire pour que vous puissiez prendre une décision entre «OK, je m'en fiche» et «Hmm, laissez-moi réfléchir un peu».

Paul Mclachlan
la source