2009-08-10 7 views
13

Est-ce que java.util.concurrent.atomic.AtomicBoolean ne possède pas une méthode qui peut inverser/inverser la valeur de manière atomique? Puis-je le faire d'une autre manière? Est-ce que je manque quelque chose?Est-ce que AtomicBoolean n'a pas de méthode negate()?

+3

Je ne comprends pas ce qui ne va pas avec booléen b = ab.get(); ab.compareAndSet (b,! b); Vous êtes assuré de retourner ce que vous avez dans le get. Est-il vraiment commun de vouloir retourner un booléen indépendamment de sa valeur actuelle à tout moment? – Yishai

+2

@Yishai: Vous êtes assuré de retourner ce que vous avez dans le get, mais disons que vous aviez 'ab = true' et il y avait 2 togglers qui avaient une condition de course. Celui qui est arrivé là a d'abord basculé le morceau, et le second a dit "oh, j'ai déjà été basculé" et donc laissé seul. Cela laisserait 'ab = false', alors que si les opérations de bascule se produisaient séparément, cela laisserait' ab = true'. C'est un bug. Quant à savoir s'il est vraiment courant de basculer un booléen: C'est très courant dans le cas du matériel, pas sûr des situations purement logicielles. –

+0

@Jason S, je pense que vous répétez comme une question positive, au moins dans le matériel (et donc la simulation de matériel si rien d'autre), vous auriez besoin de cela. Mais je ne sais pas si c'est suffisant pour une API, cependant. – Yishai

Répondre

12

Ma mise en œuvre naïve serait ceci:

boolean v; 
do { 
    v=atomicBoolean.get(); 
} while(!atomicBoolean.compareAndSet(v, !v)); 
+0

Les battements doivent se synchroniser :) – pjp

+1

Non, ce n'est pas le cas parce que la bascule n'est pas atomique. La valeur de AtomicBoolean peut changer entre l'appel de get() et l'appel de compareAndSet() – skaffman

+3

@skaffman: auquel cas le compareAndSet() échouera, retournera false et la boucle sera de nouveau entrée. Corrigez-moi si j'ai quelque chose de mal. –

12

peu vieux ... mais ne me sentais pas vraiment les réponses ont été formidables.

Je serais totalement en désaccord avec le fait que ce n'est pas commun ou seulement utile dans le matériel. Vous pouvez vouloir un certain nombre de threads pour basculer sur une seule variable avec la même probabilité ... J'ai utilisé l'AtomicLong pour faire un faux booléen. Cela a été adopté à partir d'un JMS MessageListener que j'avais besoin de répondre à un message particulier la moitié du temps et un autre type l'autre moitié.

public class Mock { 
    private static AtomicLong count = new AtomicLong(0); 

    public boolean respond() { 
     long currentCount = count.getAndIncrement(); 

     if (currentCount % 2 == 0) { 
      return true; 
     } else { 
      return false; 
     } 
    } 
} 
0

En utilisant la méthode AtomicBoolean#compareAndSet() et une boucle while vous pouvez mettre en œuvre une méthode pour changer la valeur d'un AtomicBoolean d'une manière thread-safe comme ceci:

public static boolean negate(AtomicBoolean ab) { 
    // get the oposite value 
    boolean newVal = !ab.get(); 

    // try to set the new value if the current value is the oposite of the new value 
    while (!ab.compareAndSet(!newVal, newVal)) { 
     // if the value we try to set was already set in the mean-time 
     // then toggle the new value and try again 
     newVal = !newVal; 
    } 
    // return the value we finally could set 
    return newVal; 
} 
+0

@jtahlborn Je pense que vous avez tort. J'ai fait un test avec 50 threads simultanés essayant de basculer le AtomicBoolean 10.000 fois en attendant au hasard entre chaque itération pendant 0-5ms. Tous les threads terminés. Voulez-vous expliquer la situation dans laquelle vous pensez que cette méthode ne serait pas complète? La différence entre ma mise en œuvre et celle actuellement la mieux notée est que mine ne fait pas l'appel inutile et cher #get() dans chaque itération de la boucle while mais à la place il bascule la valeur locale et essaie ensuite de la définir. – sebthom

+0

L'autre solution utilise la valeur actuelle pour avoir un choix légitime quant à la configuration.vous "devinez" que la nouvelle valeur devrait être le contraire. cela peut être vrai, cela peut être faux. "jamais terminé" n'était probablement pas une déclaration très précise. cependant, votre version "devine" à la valeur correcte (possibilité de succès plus rapide ou échec parasite), l'autre version utilise l'information courante pour choisir la valeur suivante (un autre appel avec une espérance de succès plus élevée). Je suppose que cela dépend du mode de fonctionnement que vous préférez. – jtahlborn

+0

@jtahlborn Dans les versions de Nichola, le .get() et l'appel .compareAndSet() ne sont pas exécutés dans une seule opération atomique, ainsi la valeur de .get() peut déjà être obsolète au moment où .compareAndSet() est appelé, donc a la même probabilité d'être la bonne valeur que dans mon cas où je suis simplement en train de basculer la valeur locale et d'essayer à nouveau, ce qui donne une lecture volatile par itération. – sebthom

0

La solution proposée dans le livre The CERT Oracle Secure Coding Standard for Java est le suivant:

import java.util.concurrent.atomic.AtomicBoolean; 

final class Flag { 
    private AtomicBoolean flag = new AtomicBoolean(true); 

    public void toggle() { 
     boolean temp; 
     do { 
      temp = flag.get(); 
     } while(!flag.compareAndSet(temp, !temp)); 
    } 
} 
Questions connexes