Supposons que j'ai le code suivant:

AtomicBoolean condition;
condition = new AtomicBoolean(false);
(...)
while(!condition.get()){
   // do some stuff
}

Je sais que condition.get() est atomique, mais est-ce que !condition.get() est également atomique?

Je veux dire, pourrait-il arriver qu'un Thread lise la valeur booléenne de manière atomique et ensuite il a été interrompu avant d'appliquer l'opération !, de sorte qu'un autre Thread entre dans la boucle avant? Si tel est le cas, serait-il préférable d'utiliser une fonction telle que:

private synchronized boolean checkNegatedCondition(AtomicBoolean cond){
    return !cond.get();
}

(...)

while(checkNegatedCondition(condition)){
   // do some stuff
}

Merci d'avance.

3
yet_another_programmer 14 août 2017 à 14:52

2 réponses

Meilleure réponse

L'atomicité n'est pas le problème. Deux threads peuvent lire une variable atomique simultanément. Ainsi, n'importe quel nombre de threads pourrait lire la valeur et entrer dans la boucle en fonction de sa négation. Peu importe si! est atomique. Il agit sur une valeur locale au thread.

Pour utiliser atomics pour l'exclusion, vous avez généralement besoin de AtomicBoolean.compareAndSet(boolean,boolean).

v.compareAndSet(a,b) ne définira v sur la valeur b que si elle est a et renvoie vrai. Sinon, si v n'a pas la valeur a au début, il ne fait rien et renvoie false.

En pseudo-code c'est

 synchronized public boolean compareAndSet(boolean a,boolean b){
      if(v!=a) return false;
      v=b;
      return true;
 }

Mais (très important) a lieu de manière atomique, donc toutes les autres opérations sur v peuvent être ordonnées avant ou après.

AtomicBoolean condition = new AtomicBoolean(false);
//...
if(condition.compareAndSet(false,true)){
   //Only one thread can get here unless condition is set back...
}

Si vous vouliez l'utiliser comme un bloc synchronisé `` pseudo '', vous pouvez coder:

while(!condition.compareAndSet(false,true));
   //Only one thread can get here at a time...
 condition.set(false);

Cela pourrait être décrit comme un verrouillage «pin». Parce que les fils «en attente» pour entrer dans la section contrôlée «tournent» en rond dans la boucle jusqu'à ce qu'ils soient libérés. Cela peut montrer des performances inférieures à synchronized car ce système d'exploitation "suspendra" normalement les threads en attente et fera progresser d'autres tâches. Cependant, les verrous de rotation peuvent montrer de meilleures performances dans les cas où la contention est très faible (c'est-à-dire que peu ou pas d'attente est nécessaire).

Conseil de pro: En pratique, il est recommandé de placer le bloc condition.set(false) in a enfin`:

while(!condition.compareAndSet(false,true));
try {
   //Only one thread can get here at a time...
 }finally{
     condition.set(false);
 }

Sinon, une exception lancée dans un thread donnera définitivement lock out tout accès à cette section de code.

3
Persixty 14 août 2017 à 17:40

AtomicBoolean.get ne consiste pas à être atomique, mais plutôt à être thread-safe.

Atomic serait un concept lié à la lecture et à l'écriture de trucs, et votre question ne mentionne rien de tel.

Si vous voulez lire et écrire (ou écrire et lire) de manière atomique, il existe d'autres méthodes sur AtomicBoolean (comme getAndSet ou compareAndSet). D'après votre question, il peut sembler que vous recherchez quelque chose de plus lié à cela (vous ne pouvez pas le dire avec certitude à partir des détails de votre question).

Si ce que vous voulez n'est pas de récupérer et de modifier une valeur de manière atomique, mais de la récupérer jusqu'à ce qu'elle soit modifiée à un autre endroit de votre code après une certaine logique (donc pas de manière atomique), alors vous recherchez un verrouillage.

0
albert_nil 14 août 2017 à 12:22