Comment vous assurez-vous que plusieurs threads peuvent accéder en toute sécurité à un champ de classe?

StackOverflow https://stackoverflow.com/questions/118371

Question

Lorsqu’un champ de classe est accédé via une méthode getter par plusieurs threads, comment maintenez-vous la sécurité des threads? Le mot clé synchronisé est-il suffisant?

Est-ce sûr:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

ou le passeur introduit-il des complications supplémentaires?

Était-ce utile?

La solution

Si vous utilisez également 'synchronized' sur le setter, ce code est threadsafe. Cependant, il peut ne pas être suffisamment granulaire; Si vous disposez de 20 getters et setters et qu'ils sont tous synchronisés, vous créez peut-être un goulot d'étranglement de synchronisation.

Dans ce cas spécifique, avec une seule variable int, éliminer le mot "synchronized" et marquer le champ int comme "volatile" assurera également la visibilité (chaque thread verra la dernière valeur de "val" lors de l'appel du getter) mais il se peut que la synchronisation ne soit pas suffisante pour répondre à vos besoins. Par exemple, attendre

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

pour définir val à 2 si et seulement si c'est déjà 1 est incorrect. Pour cela, vous avez besoin d’un verrou externe ou d’une méthode de comparaison atomique.

Je vous suggère fortement de lire et al de Brian Goetz , qui dispose de la concurrence de Java dans la pratique , qui offre la meilleure couverture possible des constructions de concurrence de Java.

Autres conseils

En plus de Commentaire de Cowan , vous pouvez effectuer les opérations suivantes pour comparer et stocker:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

Cela fonctionne car le verrou défini via une méthode synchronisée est implicitement identique au verrou de l'objet ( voir les spécifications de langue Java ).

D'après ce que j'ai compris, vous devriez utiliser synchronisé à la fois sur les méthodes getter et setter, et cela suffit.

Modifier: voici un lien vers des informations supplémentaires sur la synchronisation et ce qui ne l'est pas.

Si votre classe ne contient qu'une seule variable, un autre moyen de sécuriser les threads consiste à utiliser l'objet AtomicInteger existant.

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

Cependant, si vous ajoutez des variables supplémentaires telles qu'elles sont dépendantes (l'état d'une variable dépend de l'état d'une autre), AtomicInteger ne fonctionnera pas.

En écho à la suggestion de lire "Java Concurrency in Practice".

Pour les objets simples, cela peut suffire. Dans la plupart des cas, évitez le mot clé synchronized car vous risquez de vous trouver dans une impasse de synchronisation.

Exemple:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

Assure qu'un seul thread lit ou écrit sur le membre d'instance locale.

Lisez le livre "Programmation concurrente en Java (tm): principes et modèles de conception (Java (Addison-Wesley))", peut-être http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html est également utile .. .

La synchronisation existe pour protéger contre les interférences de thread et les erreurs de cohérence de la mémoire. En se synchronisant sur getVal (), le code garantit que les autres méthodes synchronisées sur SomeClass ne s'exécutent pas également au même moment. Puisqu'il n'y a pas d'autres méthodes synchronisées, cela ne fournit pas beaucoup de valeur. Notez également que les lectures et écritures sur les primitives ont un accès atomique. Cela signifie qu'avec une programmation soignée, il n'est pas nécessaire de synchroniser l'accès au terrain.

Lisez la Synchronisation .

Je ne sais pas vraiment pourquoi cela a été abandonné à -3. Je résume simplement ce que dit le tutoriel sur la synchronisation de Sun (ainsi que ma propre expérience).

  

Utiliser un simple accès aux variables atomiques est   plus efficace que d'accéder à ces   variables par code synchronisé,   mais nécessite plus de soin par le   programmeur pour éviter la cohérence de la mémoire   les erreurs. Si l'effort supplémentaire est   la peine dépend de la taille et   complexité de l'application.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top