AtomicInteger lazySet vs set

Quelle est la différence entre les méthodes lazySet et set de 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 soit pas immédiatement définie à la valeur souhaitée, mais sera programmée pour être définie ultérieurement. Mais, quelle est l’utilisation pratique de cette méthode? Un exemple?

Cité directement depuis le bug 6275329 :

Comme probablement le dernier petit suivi de JSR166 pour Mustang, nous avons ajouté une méthode “lazySet” aux classes Atomic (AtomicInteger, AtomicReference, etc.). Cette méthode de niche est parfois utile lors de la mise au point 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 par les autres threads) jusqu’à ce qu’une autre action volatile d’écriture ou de synchronisation se produise.

Le principal cas d’utilisation consiste à annuler les champs de nœuds dans des structures de données non bloquantes uniquement pour éviter la rétention de déchets à long terme; il s’applique quand il est inoffensif si d’autres threads voient des valeurs non nulles pendant un moment, mais vous voudriez vous assurer que les structures sont éventuellement GCable. Dans de tels cas, vous pouvez obtenir de meilleures performances en évitant les coûts de l’écriture volatile null. Il existe également quelques autres cas d’utilisation de ce type pour les systèmes atomiques non basés sur des références. La méthode est donc prise en charge dans toutes les classes AtomicX.

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

LazySet peut être utilisé pour la communication rmw inter thread, car xchg est atomique, quant à la visibilité, lorsque le processus thread threader modifie un emplacement de ligne de cache, le processeur du thread thread le verra à la lecture suivante, car le protocole de cohérence de cache LazySet fonctionne, mais la ligne de cache sera mise à jour à la lecture suivante, 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 peuvent “espionner” le bus d’adresse pour les access d’autres processeurs à la mémoire système et à leurs caches internes. Ils utilisent cette fonction de surveillance pour garder leurs caches internes cohérents à la fois avec la mémoire système et avec les caches d’autres processeurs interconnectés. Si en surveillant 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 à l’état partagé, le processeur de surveillance invalidera son bloc de cache en le forçant à remplir une ligne de cache .

oracle hotspot jdk pour l’architecture cpu x86->

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

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 producteur unique, de queue de consommateur, la barrière logicielle xchg peut forcer la ligne de codes avant le lazySet (séquence + 1) pour le thread producteur à se produire AVANT tout code de thread consommateur qui consumra (travaillera) les nouvelles données Le thread consommateur devra vérifier de manière atomique que la séquence du producteur a été incrémentée exactement par une compareAndSet (séquence, séquence + 1).

J’ai suivi le code source de Hotspot pour trouver le mappage exact du code lazySet dans le 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?

Une discussion plus large des origines et de 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ù elle agit comme une banque de stockage et non comme une barrière de stockage. Cela revient à lazySet étant JIT compilé en une instruction MOV qui ne peut pas être ré-ordonnée par le compilateur plutôt que l’instruction beaucoup 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 graveur un mécanisme d’écriture volatile cohérent, c.-à-d. qu’il est parfaitement légitime qu’un seul écrivain utilise lazySet pour incrémenter un compteur, plusieurs threads incrémentant le même compteur devront résoudre les écritures concurrentes avec CAS. les couvertures d’Atomic * pour incAndGet.

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

LazySet a les effets mémoire d’écrire (assigner) une variable volatile, sauf qu’elle permet des réordonnances avec des actions de mémoire ultérieures (mais pas précédentes) qui n’imposent pas elles-mêmes de contraintes de réorganisation avec des écritures non volatiles ordinaires. Parmi les autres contextes d’utilisation, lazySet peut s’appliquer lors de la suppression de la mémoire, pour des raisons de récupération de place, une référence jamais à nouveau consultée.

Si vous êtes curieux de savoir sur lazySet, vous devez vous aussi d’autres explications

Les effets de mémoire pour les access et les mises à jour d’atomes suivent généralement les règles relatives aux volatiles, comme indiqué à 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 mémoire d’écrire (assigner) une variable volatile.

LazySet a les effets mémoire d’écrire (assigner) une variable volatile, sauf qu’elle permet des réordonnances avec des actions de mémoire ultérieures (mais pas précédentes) qui n’imposent pas elles-mêmes de contraintes de réorganisation avec des écritures non volatiles ordinaires. Parmi les autres contextes d’utilisation, lazySet peut s’appliquer lors de la suppression de la mémoire, pour des raisons de récupération de place, une référence jamais à nouveau consultée.

faiblesseCompareAndSet lit et écrit de manière atomique une variable, mais ne crée pas de classement avant, et n’offre donc aucune garantie en ce qui concerne les lectures et écritures précédentes ou ultérieures de toutes les variables autres que la cible de forteCompareAndSet. compareAndSet et toutes les autres opérations de lecture et de mise à jour telles que getAndIncrement ont les effets de mémoire de lire et d’écrire des variables volatiles.

Voici ma compréhension, corrigez-moi si je me trompe: vous pouvez penser à lazySet() comme étant “semi” volatile: il s’agit essentiellement d’une variable non volatile en termes de lecture par d’autres threads, c.-à-d. à d’autres threads. Mais il devient volatil quand une autre opération d’écriture se produit (peut provenir d’autres threads). Le seul impact de lazySet que je peux imaginer est compareAndSet . Donc, si vous utilisez lazySet() , get() provenant d’autres threads peut toujours obtenir l’ancienne valeur, mais compareAndSet() aura toujours la nouvelle valeur car il s’agit d’une opération d’écriture.

Re: essayer de le réduire –

Vous pouvez penser à ceci comme un moyen de traiter un champ volatile comme s’il n’était pas volatil pour une opération de magasin particulière (par exemple: ref = null;).

Ce n’est pas parfaitement exact, mais cela devrait suffire pour que vous puissiez prendre une décision entre “OK, je m’en fiche vraiment” et “Hmm, laisse-moi réfléchir un peu”.