2010-09-03 35 views
3

L'utilisation de volatiles sur une variable réduit le risque d'erreur de cohérence de la mémoire (veuillez me corriger si cela révèle des lacunes dans ma compréhension de tout concept pertinent). Ainsi, dans l'exemple suivant, même si la variable c1 est volatile, toujours l'apparition des résultats d'erreur de constance de mémoire dans c1 devenant 15 ou parfois 14 dans la sortie plutôt que la sortie correcte 16.Utilisation de volatiles sur des variables atomiques

class Lunch implements Runnable { 

    private volatile long c1 = 0; 
    private Object lock1 = new Object(); 
    private Object lock2 = new Object(); 
    public void inc1() { 
     // synchronized(lock1) { c1 is volatile 
      c1++; 
     // } 
    } 

    public void run() { 
     try { 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
      Thread.sleep(1000); 
      inc1(); 
     } 
     catch(InterruptedException e) { 
      return; 
     } 
    } 
    public long value() { 
     return c1; 
    } 
    public static void main(String args[]) throws InterruptedException { 
     Lunch l = new Lunch(); 
     Thread t1 = new Thread(l); 
     Thread t2 = new Thread(l); 
     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 
     System.out.println(l.value()); 
    } 
} 
+0

L'hypothèse ici est que ++ est atomique. Vérifiez vos hypothèses! – nos

+2

(Je m'inquiète un peu de votre utilisation de la phrase "réduit le risque".) Habituellement, le but est d'éviter tout risque.) –

Répondre

6

Vous avez raison. Parce que ++ n'est PAS une opération atomique, vous pouvez toujours obtenir des résultats incohérents où un thread lit/incrémente/écrit une valeur en même temps qu'un autre thread. Envisagez d'utiliser AtomicInteger pour des cas comme celui-ci.

+0

Donc, si je remplace plusieurs invocations de increment() dans run() par une autre fonction update (long c1) {this.c1 = c1} qui fait partie de cette classe. Alors puisque c1 est volatile et ++ n'est pas impliqué, le résultat devrait être toujours correct? – msk

+0

Ou 'AtomicIntegerUpdater' dans certaines circonstances où les opérations load-manipulate-store sont rares. –

+0

@msk il serait "correct" dans le sens où, à tout moment, toute lecture de thread c1 serait garantie de voir la valeur à jour de c1. L'utilité de l'information qui est définie sans être vérifiée au préalable est souvent discutable. Le cas d'utilisation habituel est un drapeau booléen où un thread en signale un autre (comme un drapeau d'arrêt). C'est à peu près le seul cas où j'utilise volatile. –

3

L'atomicité n'est qu'une partie de l'image. Il y a également visibilité. Si une valeur de variable non volatile (et non synchronisée) est modifiée, les autres threads ne sont pas garantis de voir le changement dans le temps, ou pas du tout.