Quelle est la différence entre les méthodes lazySet
et set
de AtomicInteger
? 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 sera plutôt programmée pour être définie dans le futur. Mais, quelle est l'utilité pratique de cette méthode? Un exemple?
Cité directement de "JDK-6275329: Ajouter des méthodes lazySet aux classes atomiques" :
Comme probablement le dernier petit suivi JSR166 pour 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éorganisé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 d'autres threads) jusqu'à ce qu'une autre action d'écriture volatile ou de synchronisation se produise).
Le cas d'utilisation principal consiste à annuler les champs de nœuds dans des structures de données non bloquantes uniquement pour é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 éventuellement GCables. Dans de tels cas, vous pouvez obtenir de meilleures performances en évitant les coûts de l'écriture volatile nulle. Il existe quelques autres cas d'utilisation dans ce sens pour les composants 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 les multiprocesseurs courants, lazySet fournit une barrière magasin-magasin précédente (qui est soit sans opération, soit très bon marché sur les plates-formes actuelles), mais pas de barrière de charge de magasin (qui est généralement la partie coûteuse d'une écriture volatile).
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 lecteur le verra à la prochaine lecture, car le protocole de cohérence de cache du processeur Intel garantira LazySet fonctionne, mais la ligne de cache sera mise à jour à la prochaine lecture, encore une fois, le CPU doit être suffisamment moderne.
http://sc.tamu.edu/systems/eos/nehalem.pdf Pour Nehalem qui est une plate-forme multiprocesseur, les processeurs ont la possibilité de "snoop" (espionner) le bus d'adresse pour les accès des autres processeurs à la mémoire système et à leurs caches internes. Ils utilisent cette capacité d'espionnage 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, grâce à l'espionnage, 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 en état partagé, le processeur d'espionnage 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 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 file d'attente d'un producteur, d'un consommateur, la barrière immatérielle 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) les nouvelles données, bien sûr le thread consommateur devra vérifier atomiquement que la séquence du producteur a été incrémentée d'exactement un à l'aide d'un compareAndSet (séquence, séquence + 1).
J'ai tracé après le code source 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 nouvelles choses 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 les hdis pour démonter 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 sur les origines et l'utilité de lazySet et le putOrdered sous-jacent peut être trouvée ici: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win -pour.html
Pour résumer: lazySet est une écriture volatile faible dans le sens où elle agit comme un magasin-magasin et non comme une clôture de chargement de magasin. Cela revient à lazySet étant compilé JIT en une instruction MOV qui ne peut pas être réorganisée par le compilateur plutôt que l'instruction beaucoup plus coûteuse utilisée pour un ensemble volatil.
Lorsque vous lisez 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 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 en utilisant CAS, ce qui est exactement ce qui se passe sous les couvertures d'Atomic * pour incAndGet.
À partir du Récapitulatif 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 de mémoire subséquentes (mais pas précédentes) qui n'imposent pas elles-mêmes réordonner les contraintes avec des écritures non volatiles ordinaires. Parmi d'autres contextes d'utilisation, lazySet peut s'appliquer lors de l'annulation, pour le garbage collection, d'une référence qui n'est plus jamais consultée.
Si vous êtes curieux de connaître lazySet, vous vous devez aussi d'autres explications
Les effets de mémoire pour les accès et les mises à jour 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 de mémoire de la lecture d'une variable volatile.
set a pour effet mémoire d'écrire (assigner) 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 de mémoire subséquentes (mais pas précédentes) qui n'imposent pas elles-mêmes réordonner les contraintes avec des écritures non volatiles ordinaires. Parmi d'autres contextes d'utilisation, lazySet peut s'appliquer lors de l'annulation, pour le garbage collection, d'une référence qui n'est plus jamais consultée.
faibleCompareAndSet lit atomiquement et écrit conditionnellement une variable mais ne crée aucun ordre qui se produit avant, ne fournit donc aucune garantie en ce qui concerne les lectures et écritures précédentes ou suivantes de toutes les variables autres que la cible du faiblesseCompareAndSet. compareAndSet et toutes les autres opérations de lecture et de mise à jour telles que getAndIncrement ont les effets de mémoire de la lecture et de l'écriture de variables volatiles.
Voici ma compréhension, corrigez-moi si je me trompe: vous pouvez considérer lazySet()
comme "semi" volatile: c'est fondamentalement une variable non volatile en termes de lecture par d'autres threads, c'est-à-dire la valeur définie par lazySet peut ne pas être visible par les autres threads. Mais il devient volatil lorsqu'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()
à partir 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: essayez de le baisser -
Vous pouvez considérer cela comme un moyen de traiter un champ volatile comme s'il n'était pas volatile pour une opération de stockage particulière (par exemple: ref = null;).
Ce n'est pas tout à fait exact, mais il devrait suffire que vous puissiez prendre une décision entre "OK, je m'en fiche vraiment" et "Hmm, laissez-moi y réfléchir un peu".