Volatile booléen vs AtomicBoolean


245

Que fait AtomicBoolean qu'un booléen volatil ne peut pas réaliser?


16
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.
JeffV


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.
sactiw

Réponses:


98

Ils sont juste totalement différents. Considérez cet exemple d'un volatileentier:

volatile int 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

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

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.


84
-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)


1
@ksl Je pense que @teto veut décrire que si un seul thread modifie la booleanvar, nous devons choisir volatile boolean.
znlyj

2
Excellent résumé.
Ravindra babu

56

Vous ne pouvez pas le faire compareAndSet, getAndSetcomme une opération atomique avec un booléen volatil (sauf si vous le synchronisez bien sûr).


6
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;
    return true;
} else {
    return false;
}

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:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

if (isJobDone.compareAndSet(false, true)) {
    listener.notifyJobDone();
}

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).


14

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.


14
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:

public class AtomicLong extends Number implements java.io.Serializable {
   ...
   private volatile long value;
   ...
   public final long get() {
       return value;
   }
   ...
   public final void 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.

private volatile value;
...
// race conditions here
value++;

Cependant, le code suivant fonctionnera en toute sécurité dans un environnement multi-thread sans verrous:

private final AtomicLong value = new AtomicLong();
...
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.


5

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.


5

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.


3

Rappelez-vous l'IDIOM -

LIRE - MODIFIER - ÉCRIRE ce que vous ne pouvez pas réaliser avec volatile


2
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:

  1. La valeur booléenne est lue.
  2. 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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.