11
votes

Synchronisation du fil sur une variable d'instance entier

public class Main{
    public static void main(String[] args) throws Exception {
        // Creating objects for class Check(2 different objects)
        Check c = new Check("s1");
        Check c1 = new Check("s2");
        c.start();c1.start();
   }
}
class Check extends Thread{
    Check(String name){super(name);}
    private Integer ab = 2;
    public void run(){
        synchronized (ab) {
            System.out.println(Thread.currentThread().getName());
            for(int i=0;i<10;i++)System.out.print(i+" ");
        }
    }
}
Here i have synchronized on the variable ab. And i have created two different instances of class Check also, but i am always getting output for s1 followed by s2 or vice versa, but not mixed, why is that so? when i have already created two separate objects(in main), so two different threads, two different ab variables, so how it becomes a shared resource for the two different objects?

7 commentaires

B.T.W., vous ne synchronisez pas la variable , vous synchronisez sur l'objet auquel la variable se réfère. C'est une distinction importante car certaines personnes ont commis l'erreur de ne pas realiznig que la variable peut se référer à des objets différents à des moments différents, et certains ont commis l'erreur de ne pas se rendre compte que le même objet peut être référencé par plus d'une variable.


Ce code ressemble plus à un code de test qu'un cas réel, mais si vous souhaitez synchroniser sur une sécurité sans verrouillage, mais à thread-coffre-fort, Number , vous pouvez utiliser un AtomicInteger .


@jamesLarge: Je comprends ce que vous dites. Je n'étais pas précis sur cela parce que mon problème s'est concentré sur un autre point. Quoi qu'il en soit, merci. :)


@ Jean-FrançoisAvard: Oui, c'est un code de test, aucune pertinence pour de vrais projets. Juste en train de vérifier. Oui, atomicinteger, je sais que. Merci. :)


@ Jean-François: Pour clarifier, Atomicinteger est plus une alternative au verrouillage, ce n'est pas quelque chose que vous devez utiliser comme une serrure.


@Nathanhughes true.


Dans ce cas particulier, les deux threads se synchronisent sur la même instance entier , il convient de souligner qu'il n'y a rien de mal avec deux threads apparemment en cours d'exécution l'une après l'autre. Si deux threads ne se synchronisent pas, ils n'ont aucune relation de synchronisation, pas même une garantie de fonctionnement parallèle. Il apparaît donc de fonctionner entièrement après une autre, est un résultat entièrement plausible surtout pour des threads courts comme la question de la question.


3 Réponses :


12
votes

TL; DR - C'est à cause de entier mise en commun. Faire un objet (ie objet ab = nouvel objet () ) pour garantir que chaque instance de Vérifiez . les autres.


J'étais initialement perplexe. Il est intéressant de noter que si vous changez xxx

à xxx

la synchronisation disparaît (vous obtenez des sorties différentes sur chaque course) . Retour avec AB comme integer , j'ai dirigé votre code dans le mode de débogage (avec un point d'arrêt sur la ligne de nom de thread d'impression) et trouvé ce qui suit. Voici le premier fil:

 premières variables de fil

et voici le deuxième thread.

 Deuxième variables de fil

Notez que c'est en fait le même objet, integer @ 443 . Même si vous pensiez avoir obtenu deux objets différents, les champs AB dans les deux instances de contrôle se rapportent au même objet en mémoire! Par conséquent, oui, il y a une synchronisation correcte. La question, alors, est de savoir comment dire entier AB = 2 deux fois vous obtient le même integer objet en mémoire.


en disant Integer AB = 2 , vous utilisez autoboxing , par lequel une valeur primitive (de type int ) est automatiquement convertie en type d'objet correspondant Integer . Cela équivaut à appeler la méthode de l'autoboxage Call: xxx

si nous examinons integer.valueof , nous remarquons qu'il a un pool de valeurs dans Une certaine gamme: xxx

Pour la plupart des paramètres conventionnels, ceci inclura la valeur 2 . Ainsi, vous obtenez le même entier en mémoire lorsque vous appelez cette méthode.


2 commentaires

Vraiment intéressant. Merci beaucoup. :)


La "certaine plage" est de -128 à 127 par défaut. Notez que IntegerCache.High peut être modifié. Integercache.low ne peut pas.



1
votes

Si vous examinez le bytecode, vous verriez probablement ce code:

LINENUMBER 15 L1
ALOAD 0
ICONST_2
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
PUTFIELD com/example/Check.ab : Ljava/lang/Integer;


0 commentaires

4
votes

Ceci se passe à cause de piscine de piscine à piscine en Java.

Les entiers allant de -128 et 127 (inclus) sont utilisés de la même manière que la piscine à cordes.

Ainsi, lorsque vous utilisez Integer privé AB = 2; , AB est partagé pour les deux objets de check . .

Vous pouvez utiliser la valeur> 128 ou tout autre type d'objet afin que votre code ne soit pas synchronisé.

Vous pouvez regarder des réponses ici: Pourquoi le comportement de la pool entier constant change à 127 ans? pour comprendre le concept de piscine entier.


1 commentaires

Si vous avez besoin d'objets explicitement distincts, vous devez les créer explicitement. L'utilisation d'entiers plus importants vous donnera des objets distincts dans les implémentations actuelles, mais n'est pas gauréte de le faire.