La base de données Oracle JDK Javadoc for AtomicBoolean est la suivante:
https://docs.Oracle.com/javase/8/docs/api/Java/util/concurrent/atomic/AtomicBoolean.html
Une valeur booléenne pouvant être mise à jour de manière atomique. Voir le Spécification de package Java.util.concurrent.atomic pour la description de les propriétés des variables atomiques. Un AtomicBoolean est utilisé dans des applications telles que les drapeaux mis à jour atomiquement, et ne peuvent pas être utilisés en tant que remplacement pour un booléen.
Un collègue et moi essayions de trouver un cas d'utilisation où AtomicBoolean ne peut pas être substitué et la seule chose à laquelle nous pouvons penser est qu'il existe des méthodes dans l'objet Boolean qui ne sont pas associées à AtomicBoolean.
Est-ce la seule raison ou y avait-il autre chose à l'esprit quand cela a été écrit?
Boolean
est la classe enveloppante autour de la primitive boolean
. Il peut être créé automatiquement à partir de boolean
par le compilateur (conversion de boxe) ou converti en un booléen (conversion de déballage). Ce n'est pas le cas pour AtomicBoolean
où il s'agit d'une classe distincte conçue pour des raisons de simultanéité.
Par conséquent, les deux classes ont une sémantique différente au niveau de la langue:
Boolean b = new Boolean(true);
AtomicBoolean ab = new AtomicBoolean(true);
System.out.println(true == b); // automatic unboxing of Boolean variable
System.out.println(true == ab); // compiler error
Boolean est un objet de valeur immuable. Il a été conçu pour être immuable et rendu définitif afin de le faire respecter. Java.lang.Boolean existe depuis la version 1.0.
AtomicBoolean est modifiable et est conçu pour être mis à jour afin que la valeur mise à jour soit visible dans les threads. AtomicBoolean a été introduit avec Java 5.
Ce sont des concepts totalement différents, c'est pourquoi AtomicBoolean n'a pas été conçu pour étendre le booléen. Vous ne pouvez pas substituer un objet mutable à un objet immuable sans détruire les invariants attendus du code qui les utilise. Le code s’attendant à recevoir une valeur immuable pourrait être cassé si la version atomique pouvait être passée à sa place.
Voici donc un cas d'utilisation: si AtomicBoolean était présenté comme un substitut de Boolean, vous pourriez avoir un cas où une classe créée avant ce changement pouvait raisonnablement s'attendre à ce que, dans une méthode renvoyant un booléen, il ne soit pas nécessaire de passer en défensive copie en raison du caractère booléen immuable. Si la référence renvoyée est initialisée à partir d'une source qui utilise AtomicBoolean à la place de Boolean, ce champ peut maintenant être modifié en appelant la méthode renvoyant Boolean, en le convertissant en AtomicBoolean.
Les classes atomiques sont conçues pour fonctionner avec des mises à jour simultanées (en tant qu'amélioration de volatile
), mais le moyen le plus efficace de concevoir du code concurrent consiste à utiliser des valeurs immuables. Veillez donc à ne pas confondre AtomicBoolean avec "le booléen que vous utilisez lors de l'écriture de code multithread".
Un cas d'utilisation est que AtomicBoolean
ne s'étend pas Boolean
. Donc, si vous avez une méthode telle que:
void foo (Boolean b) {
doStuff();
}
Ensuite, vous ne pouvez pas passer une AtomicBoolean
comme paramètre à foo
. (Vous devez appeler la méthode get()
de AtomicBoolean
.)
Ils ne sont pas automatiquement configurables, ils ne peuvent donc pas être utilisés dans les conditions conditionnelles, par exemple,
// Explodey
if (someAtomicBoolean) {
}
Exemple:
void doSomething( final Boolean flag ) {
final boolean before = flag.booleanValue();
do0( flag );
final boolean after = flag.booleanValue();
assert before == after;
if ( flag.booleanValue() ) {
do1();
}
if ( flag.booleanValue() ) {
do2();
}
}
peut donner un résultat différent de celui
void doSomething( final AtomicBoolean flag ) {
final boolean before = flag.get();
do0( flag );
final boolean after = flag.get();
assert (before == after) || (before != after);
if ( flag.get() ) {
do1();
}
if ( flag.get() ) {
do2();
}
}
parce que AtomicBoolean
peut changer sa valeur, alors que Boolean
ne le peut pas.
Dans le premier cas, do1()
et do2()
sont tous deux appelés ou aucun d’entre eux.
Dans le second cas, les deux, ou aucun d'entre eux, ne peuvent être appelés si la valeur de AtomicBoolean
est modifiée simultanément.
Parce que Boolean
a toujours été présent et a toujours été défini comme immuable, AtomicBoolean
, introduit beaucoup plus tard, ne peut pas être substitué à Boolean
car il se comporte différemment et un code reposant légitimement sur l'immutabilité d'un Boolean
peut s'effondrer si cette immutabilité est détruite.
Notez que Boolean
ne peut pas être remplacé par AtomicBoolean
et vice versa . Ils ne sont tout simplement pas compatibles dans leur sémantique.
Parce que Boolean
est immuable. Voir: Pourquoi une classe Wrapper comme Boolean en Java est immuable? et ma réponse:
Parce que 2
est 2. Ce ne sera pas 3
demain.
Immutable est toujours préféré comme valeur par défaut surtout dans les situations multithread, ce qui facilite la lecture et la maintenance du code. Exemple: l’API Java Date
, qui présente de nombreux défauts de conception. Si Date
était immuable, l'API serait très simplifiée. Je saurais que les opérations Date
créeraient de nouvelles dates et n'auraient jamais à rechercher des API qui les modifient.
Lisez La concurrence dans la pratique pour comprendre la véritable importance des types immuables.
Notez également que si, pour une raison quelconque, vous voulez des types mutables, utilisez AtomicInteger
AtomicBoolean
, etc. Pourquoi Atomic
? Parce qu'en introduisant la mutabilité, vous avez introduit un besoin de sécurité threads. Ce dont vous n'auriez pas besoin si vos types restaient immuables. Par conséquent, si vous utilisez des types mutables, vous devez également payer le prix de la réflexion sur la sécurité du thread et de l'utilisation des types du package concurrent
. Bienvenue dans le monde merveilleux de la programmation simultanée.
De même, pour Boolean
- Je vous mets au défi de nommer une opération unique que vous voudrez peut-être effectuer, que le traitement booléen soit modifiable ou non. réglé sur true? Utilisez myBool = true
. C'est une réaffectation, pas une mutation. Nier? myBool = !myBool
. Même règle. Notez que l'immutabilité est un feature, pas une contrainte, donc si vous pouvez l'offrir, vous devriez - et dans ces cas, bien sûr, vous le pouvez.
Notez que cela s'applique également à d'autres types. La chose la plus subtile avec les entiers est count++
, mais ce n'est que count = count + 1
, à moins que vous ne vouliez obtenir la valeur de manière atomique ... auquel cas utilisez la variable mutable AtomicInteger
.
Le premier cas d'utilisation principal d'opérations atomiques (parfois encapsulées) dans n'importe quel langage est la sémantique de comparaison et d'échange, un élément constitutif fondamental des applications concurrentes.
La deuxième utilisation principale consiste à masquer la complexité du placement correct des barrières de mémoire, requises pour la sémantique du modèle de mémoire.
En Java, Atomic * encapsule les deux éléments ci-dessus, le premier - avec le code natif spécifique à la plate-forme, et le second à l'aide du mot clé volatile
.