Je cherchais une réponse plus nuancée à: "quelles sont les limites de chacun?". Par exemple, si is est un indicateur défini par un thread et lu par un ou plusieurs autres, il n'est pas nécessaire d'utiliser AtomicBoolean. Cependant, comme je le vois avec ces réponses, si un thread partage une variable dans plusieurs threads peuvent écrire et agissent sur le résultat de leurs lectures, AtomicBoolean met en jeu des opérations non verrouillables de type CAS. J'apprends pas mal ici, en fait. Espérons que d'autres en bénéficieront également.
les booléens volatils nécessiteront une synchronisation explicite pour gérer les conditions de concurrence, en d'autres termes, un scénario comme une ressource partagée mise à jour (changement d'état) par plusieurs threads, par exemple incrémenter / décrémenter le compteur ou inverser les booléens.
Ils sont juste totalement différents. Considérez cet exemple d'un volatileentier:
volatileint i =0;void incIBy5(){
i +=5;}
Si deux threads appellent la fonction simultanément, ipeut être 5 par la suite, car le code compilé sera quelque peu similaire à ceci (sauf que vous ne pouvez pas synchroniser dessus int):
void incIBy5(){int temp;synchronized(i){ temp = i }synchronized(i){ i = temp +5}}
Si une variable est volatile, chaque accès atomique à celle-ci est synchronisé, mais il n'est pas toujours évident de savoir ce qui est réellement considéré comme un accès atomique. Avec un Atomic*objet, il est garanti que chaque méthode est "atomique".
Ainsi, si vous utilisez un AtomicIntegeret getAndAdd(int delta), vous pouvez être sûr que le résultat sera 10. De la même manière, si deux threads annulent booleansimultanément une variable, avec un AtomicBooleanvous pouvez être sûr qu'elle a la valeur d'origine par la suite, avec un volatile boolean, vous ne pouvez pas.
Ainsi, chaque fois que vous avez plusieurs threads modifiant un champ, vous devez le rendre atomique ou utiliser une synchronisation explicite.
Le but de volatileest différent. Considérez cet exemple
Si vous avez un thread en cours d'exécution loop()et un autre thread appelant stop(), vous pouvez exécuter une boucle infinie si vous omettez volatile, car le premier thread peut mettre en cache la valeur d'arrêt. Ici, le volatilesert d'indicateur au compilateur pour être un peu plus prudent avec les optimisations.
-1: vous donnez des exemples mais n'expliquez pas vraiment la différence entre un volatile et un Atomicxxxx.
Jason S
70
La question ne concerne pas volatile. La question est sur le point volatile booleanvs AtomicBoolean.
dolmen
26
-1: la question spécifiquement posée sur booléen, qui est un cas unique par rapport aux autres types de données et doit être expliquée directement.
John Haager
8
@ sgp15 Cela concerne la synchronisation à partir de Java 5.
Man of One Way
6
Si la valeur booléenne est lue par plusieurs threads, mais écrite par un seul thread, cela volatile booleansuffit. S'il y a aussi de nombreux écrivains, vous en aurez peut-être besoin AtomicBoolean.
StvnBrkdll
263
J'utilise des champs volatils lorsque ce champ est UNIQUEMENT MIS À JOUR par son thread propriétaire et que la valeur n'est lue que par d'autres threads, vous pouvez le considérer comme un scénario de publication / abonnement où il y a de nombreux observateurs mais un seul éditeur. Cependant, si ces observateurs doivent exécuter une logique basée sur la valeur du champ, puis repousser une nouvelle valeur, je choisis des vars ou des verrous Atomic * ou des blocs synchronisés, selon ce qui me convient le mieux. Dans de nombreux scénarios simultanés, il se résume à obtenir la valeur, à la comparer à une autre et à la mettre à jour si nécessaire, d'où les méthodes compareAndSet et getAndSet présentes dans les classes Atomic *.
Consultez les JavaDocs du package java.util.concurrent.atomic pour obtenir une liste des classes Atomic et une excellente explication de leur fonctionnement (nous venons d'apprendre qu'elles sont sans verrouillage, afin qu'elles aient un avantage sur les verrous ou les blocs synchronisés)
C'est vrai, mais ne serait-ce pas une exigence assez rare pour un booléen?
Robin
1
@Robin pense à l'utiliser pour contrôler un appel paresseux d'une méthode d'initialisation.
Ustaman Sangat
En fait, je pense que c'est l'un des principaux cas d'utilisation.
fool4jesus
42
AtomicBooleana des méthodes qui effectuent leurs opérations composées de manière atomique et sans avoir à utiliser un synchronizedbloc. D'un autre côté, volatile booleanne peut effectuer des opérations composées que si cela se fait dans un synchronizedbloc.
Les effets de mémoire de la lecture / écriture sur volatile booleansont identiques à getet aux setméthodes de AtomicBooleanrespectivement.
Par exemple, la compareAndSetméthode exécutera atomiquement ce qui suit (sans synchronizedbloc):
if(value == expectedValue){
value = newValue;returntrue;}else{returnfalse;}
Par conséquent, la compareAndSetméthode vous permettra d'écrire du code dont l'exécution est garantie une seule fois, même lorsqu'elle est appelée à partir de plusieurs threads. Par exemple:
Est garanti de ne notifier l'auditeur qu'une seule fois (en supposant qu'aucun autre thread ne remette le AtomicBooleandos falseaprès qu'il ait été défini sur true).
volatileLe mot clé garantit une relation entre les threads partageant cette variable. Cela ne vous garantit pas que 2 threads ou plus ne s'interrompent pas lors de l'accès à cette variable booléenne.
L'accès booléen (comme dans le type primitif) est atomique en Java. Lectures et devoirs. Aucun autre thread n'interrompra donc les opérations booléennes.
Maciej Biłas
1
Je suis désolé, mais comment cela répond-il à la question? Une Atomic*classe encapsule un volatilechamp.
Gray
Le processeur n'est-il pas le principal facteur de définition de la volatilité? Pour vous assurer que la valeur lue correspond bien à sa valeur la plus récente
jocull
8
Volatile booléen vs AtomicBoolean
Les classes Atomic * encapsulent une primitive volatile du même type. De la source:
publicclassAtomicLongextendsNumberimplements java.io.Serializable{...privatevolatilelong value;...publicfinallong get(){return value;}...publicfinalvoid set(long newValue){
value = newValue;}
Donc, si tout ce que vous faites est d'obtenir et de définir un Atomic *, vous pourriez tout aussi bien avoir un champ volatil à la place.
Que fait AtomicBoolean qu'un booléen volatil ne peut pas réaliser?
Les classes Atomic * vous offrent des méthodes qui fournissent des fonctionnalités plus avancées telles que incrementAndGet(), compareAndSet()et d'autres qui implémentent plusieurs opérations (get / increment / set, test / set) sans verrouillage. C'est pourquoi les classes Atomic * sont si puissantes.
Par exemple, si plusieurs threads utilisent le code suivant à l'aide de ++, il y aura des conditions de concurrence critique en raison de ++: get, increment et set.
privatevolatile value;...// race conditions here
value++;
Cependant, le code suivant fonctionnera en toute sécurité dans un environnement multi-thread sans verrous:
privatefinalAtomicLong value =newAtomicLong();...
value.incrementAndGet();
Il est également important de noter que l'habillage de votre champ volatile à l'aide de la classe Atomic * est un bon moyen d'encapsuler la ressource partagée critique du point de vue d'un objet. Cela signifie que les développeurs ne peuvent pas simplement traiter le champ en supposant qu'il n'est pas partagé, ce qui peut entraîner des problèmes avec un champ ++; ou tout autre code introduisant des conditions de course.
S'il existe plusieurs threads accédant à la variable de niveau classe, chaque thread peut conserver une copie de cette variable dans son cache threadlocal.
Rendre la variable volatile empêchera les threads de conserver la copie de la variable dans le cache threadlocal.
Les variables atomiques sont différentes et permettent une modification atomique de leurs valeurs.
Le type primitif booléen est atomique pour les opérations d'écriture et de lecture, volatile garantit le principe passe-avant. Donc, si vous avez besoin d'un simple get () et set (), vous n'avez pas besoin de l'AtomicBoolean.
D'un autre côté, si vous devez implémenter une vérification avant de définir la valeur d'une variable, par exemple, "si vrai puis défini sur faux", vous devez également effectuer cette opération de manière atomique, dans ce cas, utilisez compareAndSet et d'autres méthodes fournies par AtomicBoolean, car si vous essayez d'implémenter cette logique avec un booléen volatile, vous aurez besoin d'une synchronisation pour vous assurer que la valeur n'a pas changé entre get et set.
Court, croustillant et précis. volatilene fonctionne que dans les cas où le thread propriétaire a la possibilité de mettre à jour la valeur du champ et les autres threads ne peuvent que lire.
Chaklader Asfak Arefe
3
Si vous n'avez qu'un seul thread modifiant votre booléen, vous pouvez utiliser un booléen volatile (vous le faites généralement pour définir une stopvariable vérifiée dans la boucle principale du thread).
Cependant, si vous avez plusieurs threads modifiant le booléen, vous devez utiliser un AtomicBoolean. Sinon, le code suivant n'est pas sûr:
boolean r =!myVolatileBoolean;
Cette opération se fait en deux étapes:
La valeur booléenne est lue.
La valeur booléenne est écrite.
Si un autre thread modifie la valeur entre #1et 2#, vous pourriez obtenir un résultat incorrect. AtomicBooleanLes méthodes évitent ce problème en faisant des étapes #1et de manière #2atomique.
"Si vous n'avez qu'un seul thread modifiant votre booléen, vous pouvez utiliser un booléen volatile." Ensuite, si vous utilisez un thread, pourquoi auriez-vous besoin de volatile (?) .. Vous devriez supprimer le premier paragraphe pour améliorer la réponse ..
minsk
-1
Les deux sont du même concept, mais en booléen atomique, il fournira l'atomicité de l'opération au cas où le commutateur CPU se produirait entre les deux.
Réponses:
Ils sont juste totalement différents. Considérez cet exemple d'un
volatile
entier:Si deux threads appellent la fonction simultanément,
i
peut être 5 par la suite, car le code compilé sera quelque peu similaire à ceci (sauf que vous ne pouvez pas synchroniser dessusint
):Si une variable est volatile, chaque accès atomique à celle-ci est synchronisé, mais il n'est pas toujours évident de savoir ce qui est réellement considéré comme un accès atomique. Avec un
Atomic*
objet, il est garanti que chaque méthode est "atomique".Ainsi, si vous utilisez un
AtomicInteger
etgetAndAdd(int delta)
, vous pouvez être sûr que le résultat sera10
. De la même manière, si deux threads annulentboolean
simultanément une variable, avec unAtomicBoolean
vous pouvez être sûr qu'elle a la valeur d'origine par la suite, avec unvolatile boolean
, vous ne pouvez pas.Ainsi, chaque fois que vous avez plusieurs threads modifiant un champ, vous devez le rendre atomique ou utiliser une synchronisation explicite.
Le but de
volatile
est différent. Considérez cet exempleSi vous avez un thread en cours d'exécution
loop()
et un autre thread appelantstop()
, vous pouvez exécuter une boucle infinie si vous omettezvolatile
, car le premier thread peut mettre en cache la valeur d'arrêt. Ici, levolatile
sert d'indicateur au compilateur pour être un peu plus prudent avec les optimisations.la source
volatile
. La question est sur le pointvolatile boolean
vsAtomicBoolean
.volatile boolean
suffit. S'il y a aussi de nombreux écrivains, vous en aurez peut-être besoinAtomicBoolean
.J'utilise des champs volatils lorsque ce champ est UNIQUEMENT MIS À JOUR par son thread propriétaire et que la valeur n'est lue que par d'autres threads, vous pouvez le considérer comme un scénario de publication / abonnement où il y a de nombreux observateurs mais un seul éditeur. Cependant, si ces observateurs doivent exécuter une logique basée sur la valeur du champ, puis repousser une nouvelle valeur, je choisis des vars ou des verrous Atomic * ou des blocs synchronisés, selon ce qui me convient le mieux. Dans de nombreux scénarios simultanés, il se résume à obtenir la valeur, à la comparer à une autre et à la mettre à jour si nécessaire, d'où les méthodes compareAndSet et getAndSet présentes dans les classes Atomic *.
Consultez les JavaDocs du package java.util.concurrent.atomic pour obtenir une liste des classes Atomic et une excellente explication de leur fonctionnement (nous venons d'apprendre qu'elles sont sans verrouillage, afin qu'elles aient un avantage sur les verrous ou les blocs synchronisés)
la source
boolean
var, nous devons choisirvolatile boolean
.Vous ne pouvez pas le faire
compareAndSet
,getAndSet
comme une opération atomique avec un booléen volatil (sauf si vous le synchronisez bien sûr).la source
AtomicBoolean
a des méthodes qui effectuent leurs opérations composées de manière atomique et sans avoir à utiliser unsynchronized
bloc. D'un autre côté,volatile boolean
ne peut effectuer des opérations composées que si cela se fait dans unsynchronized
bloc.Les effets de mémoire de la lecture / écriture sur
volatile boolean
sont identiques àget
et auxset
méthodes deAtomicBoolean
respectivement.Par exemple, la
compareAndSet
méthode exécutera atomiquement ce qui suit (sanssynchronized
bloc):Par conséquent, la
compareAndSet
méthode vous permettra d'écrire du code dont l'exécution est garantie une seule fois, même lorsqu'elle est appelée à partir de plusieurs threads. Par exemple:Est garanti de ne notifier l'auditeur qu'une seule fois (en supposant qu'aucun autre thread ne remette le
AtomicBoolean
dosfalse
après qu'il ait été défini surtrue
).la source
volatile
Le mot clé garantit une relation entre les threads partageant cette variable. Cela ne vous garantit pas que 2 threads ou plus ne s'interrompent pas lors de l'accès à cette variable booléenne.la source
Atomic*
classe encapsule unvolatile
champ.Les classes Atomic * encapsulent une primitive volatile du même type. De la source:
Donc, si tout ce que vous faites est d'obtenir et de définir un Atomic *, vous pourriez tout aussi bien avoir un champ volatil à la place.
Les classes Atomic * vous offrent des méthodes qui fournissent des fonctionnalités plus avancées telles que
incrementAndGet()
,compareAndSet()
et d'autres qui implémentent plusieurs opérations (get / increment / set, test / set) sans verrouillage. C'est pourquoi les classes Atomic * sont si puissantes.Par exemple, si plusieurs threads utilisent le code suivant à l'aide de
++
, il y aura des conditions de concurrence critique en raison de++
: get, increment et set.Cependant, le code suivant fonctionnera en toute sécurité dans un environnement multi-thread sans verrous:
Il est également important de noter que l'habillage de votre champ volatile à l'aide de la classe Atomic * est un bon moyen d'encapsuler la ressource partagée critique du point de vue d'un objet. Cela signifie que les développeurs ne peuvent pas simplement traiter le champ en supposant qu'il n'est pas partagé, ce qui peut entraîner des problèmes avec un champ ++; ou tout autre code introduisant des conditions de course.
la source
S'il existe plusieurs threads accédant à la variable de niveau classe, chaque thread peut conserver une copie de cette variable dans son cache threadlocal.
Rendre la variable volatile empêchera les threads de conserver la copie de la variable dans le cache threadlocal.
Les variables atomiques sont différentes et permettent une modification atomique de leurs valeurs.
la source
Le type primitif booléen est atomique pour les opérations d'écriture et de lecture, volatile garantit le principe passe-avant. Donc, si vous avez besoin d'un simple get () et set (), vous n'avez pas besoin de l'AtomicBoolean.
D'un autre côté, si vous devez implémenter une vérification avant de définir la valeur d'une variable, par exemple, "si vrai puis défini sur faux", vous devez également effectuer cette opération de manière atomique, dans ce cas, utilisez compareAndSet et d'autres méthodes fournies par AtomicBoolean, car si vous essayez d'implémenter cette logique avec un booléen volatile, vous aurez besoin d'une synchronisation pour vous assurer que la valeur n'a pas changé entre get et set.
la source
Rappelez-vous l'IDIOM -
LIRE - MODIFIER - ÉCRIRE ce que vous ne pouvez pas réaliser avec volatile
la source
volatile
ne fonctionne que dans les cas où le thread propriétaire a la possibilité de mettre à jour la valeur du champ et les autres threads ne peuvent que lire.Si vous n'avez qu'un seul thread modifiant votre booléen, vous pouvez utiliser un booléen volatile (vous le faites généralement pour définir une
stop
variable vérifiée dans la boucle principale du thread).Cependant, si vous avez plusieurs threads modifiant le booléen, vous devez utiliser un
AtomicBoolean
. Sinon, le code suivant n'est pas sûr:Cette opération se fait en deux étapes:
Si un autre thread modifie la valeur entre
#1
et2#
, vous pourriez obtenir un résultat incorrect.AtomicBoolean
Les méthodes évitent ce problème en faisant des étapes#1
et de manière#2
atomique.la source
Les deux sont du même concept, mais en booléen atomique, il fournira l'atomicité de l'opération au cas où le commutateur CPU se produirait entre les deux.
la source