Quand dois-je utiliser AtomicBoolean en Java?

Réponses:

245

Lorsque plusieurs threads doivent vérifier et modifier le booléen. Par exemple:

if (!initialized) {
   initialize();
   initialized = true;
}

Ce n'est pas thread-safe. Vous pouvez le corriger en utilisant AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}
Bozho
la source
51
Cela ne ressemble pas à un exemple réel - d'autres threads peuvent voir truequand initialize()ils ne sont pas terminés. Donc, cela ne fonctionne que si les autres threads ne se soucient pas de l'achèvement de initialize().
axtavt
6
@axtavt: Je pense que c'est un exemple du monde réel parfaitement valide s'il initializedest simplement utilisé pour garantir qu'un et un seul thread invoquera la initialize()méthode. Évidemment, initializedêtre vrai ne signifie pas que l'initialisation est définitivement terminée dans ce cas, donc peut - être qu'un terme légèrement différent serait mieux ici. Encore une fois, cela dépend de son utilisation.
ColinD
14
vous auriez besoin de 2 booléens pour initStarted et initCompleted, puis le premier thread définit initStarted et appelle initialise (), le reste attend que initCompleted soit vrai.
Martin
3
@Bozho - les lectures et les écritures sur les champs booléens sont-elles atomiques non ?, Maintenant, volatile me donne la dernière valeur du champ booléen. Donc, effectivement, ce ne serait pas volatile booleanla même chose que AtomicBoolean?.
TheLostMind
2
@Martin: Il n'y a aucun moyen direct d'attendre qu'un booléen devienne vrai; vous avez besoin de mécanismes supplémentaires. L'approche la plus judicieuse consiste à utiliser un synchronizedbloc, auquel cas vous n'avez plus besoin d'un AtomicBoolean, juste d'un volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }garantira qu'un seul thread appelle initializeet que tous les autres threads l'attendent, à condition que cela initializedsoit marqué volatile.)
ruakh
52

Voici les notes (du livre de Brian Goetz ) que j'ai prises, qui pourraient vous être utiles

Classes AtomicXXX

  • fournir une implémentation de comparaison et d'échange non bloquante

  • Profite de la prise en charge fournie par le matériel (l'instruction CMPXCHG sur Intel) Lorsque de nombreux threads s'exécutent dans votre code qui utilise ces API de concurrence atomique, ils évolueront bien mieux que le code qui utilise des moniteurs / synchronisation au niveau objet. Étant donné que les mécanismes de synchronisation de Java font attendre le code, lorsqu'il y a beaucoup de threads qui traversent vos sections critiques, une quantité substantielle de temps CPU est consacrée à la gestion du mécanisme de synchronisation lui-même (attente, notification, etc.). Étant donné que la nouvelle API utilise des constructions de niveau matériel (variables atomiques) et des algorithmes d'attente et de verrouillage gratuits pour implémenter la sécurité des threads, beaucoup plus de temps CPU est consacré à "faire des choses" plutôt qu'à gérer la synchronisation.

  • non seulement offrent un meilleur débit, mais ils offrent également une plus grande résistance aux problèmes de vivacité tels que l'impasse et l'inversion de priorité.

Aravind Yarram
la source
34

Il y a deux raisons principales pour lesquelles vous pouvez utiliser un booléen atomique. D'abord son mutable, vous pouvez le passer comme référence et changer la valeur associée au booléen lui-même, par exemple.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

et dans la classe someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Plus important encore, son thread est sûr et peut indiquer aux développeurs qui maintiennent la classe que cette variable devrait être modifiée et lue à partir de plusieurs threads. Si vous n'utilisez pas un AtomicBoolean, vous devez synchroniser la variable booléenne que vous utilisez en la déclarant volatile ou en la synchronisant autour de la lecture et de l'écriture du champ.

John Vint
la source
4
Pour l'amour de Dieu, ce n'était que pour montrer la mutabilité de l'objet lui-même. J'ai spécifiquement écrit cela à des fins de démonstration.
John Vint
Et plus encore, si c'était TOUT ce qui se passait alors oui, cela reviendra toujours vrai
John Vint
Cela ne prouve pas s'il est ou non thread-safe. Je peux terminer mes extraits de code pour rendre la classe très thread-safe, mais cela ne fait que tuer mon point.
John Vint
1
Je pense que seul Volatile ne suffit pas. Pensez à une situation dans laquelle deux threads qui lisent et écrivent la même valeur directement à partir de la mémoire principale, il n'y a pas de synchronisation entre ces threads - des problèmes de concurrence peuvent survenir.
Shay Tsadok
1
Vous avez raison, cela ne serait pas suffisant pour l'ensemble atomique, puis vérifiez les opérations, bien qu'il n'y ait pas assez de contexte de l'OP pour faire cette hypothèse. Dire, volatile pourrait ne pas suffire est toujours vrai selon la situation bien sûr.
John Vint
18

La AtomicBooleanclasse vous donne une valeur booléenne que vous pouvez mettre à jour atomiquement. Utilisez-le lorsque plusieurs threads accèdent à une variable booléenne.

La vue d'ensemble du package java.util.concurrent.atomic vous donne une bonne description de haut niveau de ce que font les classes de ce package et quand les utiliser. Je recommanderais également le livre Java Concurrency in Practice de Brian Goetz.

Cameron Skinner
la source
5

Extrait de la description du package

Package java.util.concurrent.atomic description: Une petite boîte à outils de classes qui prennent en charge la programmation thread-safe sans verrouillage sur des variables uniques. [...]

Les spécifications de ces méthodes permettent aux implémentations d'employer des instructions atomiques efficaces au niveau de la machine qui sont disponibles sur les processeurs contemporains. [...]

Les instances des classes AtomicBoolean, AtomicInteger, AtomicLong et AtomicReference fournissent chacune un accès et des mises à jour à une seule variable du type correspondant. [...]

Les effets de mémoire pour les accès et les mises à jour atomiques suivent généralement les règles pour les volatiles:

  • 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.
  • faibleCompareAndSet lit atomiquement et écrit conditionnellement une variable, est ordonné par rapport aux autres opérations de mémoire sur cette variable, mais agit autrement comme une opération de mémoire non volatile ordinaire.
  • 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.
OscarRyz
la source