53 votes

Pourquoi `synchronized (new Object ({Object))} {}` est-il un no-op?

Dans le code suivant:

class A {
    private int number;

    public void a() {
        number = 5;
    }

    public void b() {
        while(number == 0) {
            // ...
        }
    }
}

Si la méthode b est appelé et puis un nouveau thread est démarré, qui déclenche la méthode a, alors la méthode b n'est pas garanti jamais l'occasion de voir le changement de number et ainsi b peut ne jamais se terminer.

Bien sûr, nous pourrions faire number volatile pour résoudre ce problème. Cependant pour des raisons académiques supposons qu' volatile n'est pas une option:

La JSR-133 Faq nous dit:

Après nous sortir d'un bloc synchronisé, nous libérer le moniteur, ce qui a pour effet de vider le cache de la mémoire principale, afin que les écritures faites par ce fil peut être visible par les autres threads. Avant que nous puissions entrer dans un bloc synchronisé, nous acquérons le moniteur, ce qui a pour effet d'invalider le processeur local cache , de sorte que les variables sont rechargés à partir de la mémoire principale.

Cela sonne comme je viens de besoin à la fois d' a et b d'entrer et de sortir tout synchronized-Bloc à tous, peu importe ce que l'écran qu'ils utilisent. Plus précisément, il semble que cela...:

class A {
    private int number;

    public void a() {
        number = 5;
        synchronized(new Object()) {}
    }

    public void b() {
        while(number == 0) {
            // ...
            synchronized(new Object()) {}
        }
    }
}

...permettrait d'éliminer le problème et vous aurez la garantie que b va voir le changement d' a , et donc aussi éventuellement mettre fin.

Cependant la Faq aussi indiquer clairement:

Une autre implication est que le modèle suivant, qui certaines personnes utiliser pour forcer une barrière de mémoire, ne fonctionne pas:

synchronized (new Object()) {}

C'est en fait un no-op, et le compilateur peut la supprimer entièrement, parce que le compilateur sait qu'aucun autre thread ne va synchroniser sur le même moniteur. Vous disposez d'un passe-avant la relation pour un thread pour voir les résultats de l'autre.

Maintenant que c'est déroutant. Je pensais que la synchronisation Instruction de la cause des caches de chasse d'eau. Il ne peut certainement pas de vider le cache de la mémoire principale de façon à ce que les modifications dans la mémoire principale ne peut être vu que par des fils qui se synchroniser sur le même écran, surtout que depuis volatile qui se fait de la même chose que nous n'avons pas besoin d'un moniteur, ou je me trompe? Alors pourquoi est-ce un no-op et ne cause pas d' b à la fin de garantie?

49voto

yshavit Points 15028

La FAQ n'est pas l'autorité sur la matière; le JLS est. Section 17.4.4 spécifie synchronise avec les relations, ce qui se passe-avant les relations (17.4.5). Pertinentes du point de puce est:

  • Une action de déverrouillage sur le moniteur m synchronise avec tous les verrouillage des actions sur m (où "après" est défini en fonction de l'ordre de synchronisation).

Puisque m est ici la référence à l' new Object(), et il n'est jamais stockage ou la publication de tout autre thread, on peut être sûr qu'aucun autre thread va acquérir un verrou sur m après le verrouillage de ce bloc est libéré. En outre, puisque m est un nouvel objet, on peut être sûr qu'il n'y a pas d'action qui déjà déverrouillé. Par conséquent, nous pouvons être sûr qu'aucune action formellement synchronise avec cette action.

Techniquement, vous n'avez même pas besoin de faire un vidage du cache pour être à la hauteur JLS spec; c'est plus que l'JLS exige. Un typique de la mise en œuvre, parce que c'est la chose la plus facile, le matériel vous permet de le faire, mais ça va "au-dessus et au-delà" pour ainsi dire. Dans les cas où échapper à l'analyse indique un compilateur d'optimisation que nous avons besoin d'encore moins, le compilateur peut effectuer moins. Dans votre exemple, d'échapper à l'analyse peut indiquer au compilateur que l'action n'a aucun effet (en raison du raisonnement ci-dessus) et peut être optimisé entièrement.

20voto

james large Points 2460

le modèle suivant, qui certaines personnes utilisent pour forcer une barrière de mémoire, ne fonctionne pas:

Il n'est pas garanti d'être un no-op, mais la spécification permet d'être un no-op. La spécification exige seulement la synchronisation d'établir un passe-avant la relation entre les deux threads lorsque les deux fils de synchroniser sur le même objet, mais en fait il serait plus facile à mettre en œuvre une JVM où l'identité de l'objet n'a pas d'importance.

Je pensais que la synchronisation Instruction de la cause de vider les caches

Il n'y a pas de "cache" dans le Langage Java Spécification. C'est un concept qui n'existe que dans les détails de certains (bien, O. K., presque tous) les plates-formes matérielles et de la JVM des implémentations.

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