109 votes

La synchronisation de la non-finale de terrain

Un avertissement est en montrant à chaque fois que je synchroniser sur un non définitif champ de la classe. Voici le code:

public class X  
{  
   private Object o;  

   public void setO(Object o)  
   {  
     this.o = o;  
   }  

   public void x()  
   {  
     synchronized (o) // synchronization on a non-final field  
     {  
     }  
   }  
 } 

j'ai donc changé le codage de la manière suivante..

 public class X  
 {  

   private final Object o;       
   public X()
   {  
     o = new Object();  
   }  

   public void x()  
   {  
     synchronized (o)
     {  
     }  
   }  
 }  

Je ne suis pas sûr que le code ci-dessus est la bonne façon de le synchroniser sur un non définitif champ de la classe. Comment puis-je synchroniser un non définitif?

146voto

aioobe Points 158466

Tout d'abord, je vous encourage à essayer vraiment difficile de traiter avec des problèmes de concurrence sur un niveau plus élevé, c'est à dire de le résoudre à l'aide de classes de java.util.simultanées telles que ExecutorServices, Callables, à Terme, etc.

Cela étant dit, il n'y a rien de mal avec la synchronisation sur un non-finale de domaine en soi. Vous avez juste besoin de garder à l'esprit que si la référence de l'objet de modifications, la même section de code peuvent être exécutées en parallèle. I. e., si un thread exécute le code dans le bloc synchronisé et quelqu'un appelle setO(...), un autre thread peut s'exécuter de la même synchronisé bloc en même temps.

Synchroniser sur chaque objet sur lequel vous avez besoin d'un accès exclusif à (ou d'un objet "garder" ceux que vous avez besoin d'un accès exclusif à l'). Si plusieurs champs de la classe, vous pourriez vouloir reconsidérer la conception et peut-être introduire un autre niveau d'abstraction.

(Il est Important de points par @Jon Skeet ci-dessous, à lire attentivement :-)

51voto

Jon Skeet Points 692016

C'est vraiment pas une bonne idée, parce que votre synchronisé blocs ne sont plus vraiment synchronisé de façon cohérente.

En supposant que la synchronisation des blocs sont destinés à faire en sorte qu'un seul thread accède à des données partagées à un moment, pensez à:

  • 1 Thread entre dans le bloc synchronisé. Yay - il a un accès exclusif aux données partagées...
  • Thread 2 appels setO()
  • Fil 3 (ou 2...) entre dans le bloc synchronisé. Eek! Il pense qu'il a un accès exclusif aux données partagées, mais le thread 1 est toujours furtling avec elle...

Pourquoi vous voulez que cela se produise? Peut-être il ya certains très spécialisés des situations où il tombe sous le sens... mais vous auriez à me présenter avec un cas d'utilisation spécifiques (avec des moyens d'atténuer le genre de scénario que j'ai donné ci-dessus) avant que je serais heureux avec elle.

13voto

Marcus Points 677

Je suis d'accord avec l'un de Jean du commentaire: Vous devez toujours utiliser un blocage définitif de mannequin tout en accédant à un non-dernière variable pour éviter des incohérences dans le cas de la variable de référence des changements. Donc dans tous les cas, et comme une première règle de base:

Règle n ° 1: Si un champ n'est pas définitive, utilisez toujours un (privé) blocage définitif de mannequin.

Raison n ° 1: Vous maintenez le verrou et le changement de la variable de référence par vous-même. Un autre thread en attente à l'extérieur de la synchronisation de verrouillage sera en mesure d'entrer dans le gardé bloc.

Raison n ° 2: Vous maintenez le verrou et un autre thread changements de la variable de référence. Le résultat est le même: un Autre thread peut entrer dans le gardé bloc.

Mais lors de l'utilisation d'un blocage définitif de mannequin, il y a un autre problème: Vous risquez d'obtenir des données erronées, parce que votre non-objet final ne seront synchronisées avec la RAM lors de l'appel de synchroniser(objet). Donc, c'est la deuxième règle de base:

Règle n ° 2: Lorsque le verrouillage d'un non-objet final vous avez toujours besoin de faire les deux: à l'Aide d'un blocage définitif mannequin et le verrouillage de la non-objet final pour le bien de la RAM de synchronisation. (La seule alternative sera de déclarer tous les champs de l'objet comme étant volatile!)

Ces serrures sont également appelés "imbriqués les verrous". Notez que vous devez appeler toujours dans le même ordre, sinon vous obtiendrez un mort de verrouillage:

public class X {
    private final LOCK;
    private Object o;

    public void setO(Object o){
        this.o = o;  
    }  

    public void x() {
        synchronized (LOCK) {
        synchronized(o){
            //do something with o...
        }
        }  
    }  
} 

Comme vous pouvez le voir j'écris les deux verrous directement sur la même ligne, parce qu'ils appartiennent toujours ensemble. Comme cela, vous pourriez même faire 10 imbrication de serrures:

synchronized (LOCK1) {
synchronized (LOCK2) {
synchronized (LOCK3) {
synchronized (LOCK4) {
    //entering the locked space
}
}
}
}

Notez que ce code ne cassera pas si vous venez d'acquérir un intérieur de verrouillage comme synchronized (LOCK3) par un autre fils. Mais il va se casser si vous appelez dans un autre thread quelque chose comme ceci:

synchronized (LOCK4) {
synchronized (LOCK1) {  //dead lock!
synchronized (LOCK3) {
synchronized (LOCK2) {
    //will never enter here...
}
}
}
}

Il y a seulement une solution de contournement autour de ces imbriqués les verrous lors de la manipulation de non-final champs:

Règle #2 - Alternative: Déclarer tous les champs de l'objet comme étant volatile. (Je ne parlerai pas ici sur les inconvénients de le faire, par exemple en empêchant tout stockage en x au niveau des caches de même pour les lectures, etc...)

Donc aioobe est tout à fait juste: il suffit d'utiliser java.util.de façon concomitante. Ou commencer à comprendre tout à propos de la synchronisation et de le faire par vous-même avec imbriqués les verrous. ;)

Pour plus de détails pourquoi la synchronisation sur la non-final champs pauses, regardez dans mon cas de test: http://stackoverflow.com/a/21460055/2012947

Et pour plus de détails pourquoi vous avez besoin de synchronisés à tout en raison de la RAM et des caches d'avoir un coup d'oeil ici: http://stackoverflow.com/a/21409975/2012947

1voto

NPE Points 169956

Si o ne change jamais pour la durée de vie d'une instance d' X, la deuxième version est mieux de style, indépendamment de savoir si la synchronisation est impliqué.

Maintenant, si il n'y a rien de mal avec la première version est impossible de répondre sans savoir ce qui se passe dans la classe. J'aurais tendance à être d'accord avec le compilateur qu'il n'a pas l'air enclin à l'erreur (je ne vais pas répéter ce que les autres l'ont dit).

1voto

peenut Points 1368

Juste ajouter mon grain de sel: j'ai eu cet avertissement lorsque j'ai utilisé le composant est instancié par designer, c'est donc le champ ne peut pas vraiment être définitive, parce que le constructeur ne prend paramètres. En d'autres termes, j'avais quasi-finale de champ sans le mot-clé final.

Je pense que c'est pourquoi il est juste d'avertissement: vous avez probablement fait quelque chose de mal, mais il a peut-être raison.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X