30 votes

Tester la sécurité d'initialisation des champs finaux

J'essaie simplement de tester l'initialisation de la sécurité de finale champs, tel que garanti par l'JLS. C'est pour un livre que je suis en train d'écrire. Cependant, je ne suis pas en mesure de l'obtenir pour "échouer" basé sur mon code actuel. Quelqu'un peut-il me dire ce que je fais mal, ou si c'est juste quelque chose que j'ai à courir encore et encore et puis voir un échec avec quelques malchanceux moment?

Voici mon code:

public class TestClass {

    final int x;
    int y;
    static TestClass f;

    public TestClass() {
        x = 3;
        y = 4;
    }

    static void writer() {
        TestClass.f = new TestClass();
    }

    static void reader() {
        if (TestClass.f != null) {
            int i = TestClass.f.x; // guaranteed to see 3
            int j = TestClass.f.y; // could see 0

            System.out.println("i = " + i);
            System.out.println("j = " + j);
        }
    }
}

et mon fils sont de l'appeler comme ceci:

public class TestClient {

    public static void main(String[] args) {

        for (int i = 0; i < 10000; i++) {
            Thread writer = new Thread(new Runnable() {
                @Override
                public void run() {
                    TestClass.writer();
                }
            });

            writer.start();
        }

        for (int i = 0; i < 10000; i++) {
            Thread reader = new Thread(new Runnable() {
                @Override
                public void run() {
                    TestClass.reader();
                }
            });

            reader.start();
        }
    }
}

J'ai couru ce scénario beaucoup, beaucoup de fois. Mes boucles de courant sont de frai de 10 000 threads, mais je l'ai fait avec ce 1000, 100000, et même un million. Toujours pas d'échec. Je vois toujours les 3 et 4 pour les deux valeurs. Comment puis-je obtenir ce à l'échec?

20voto

Jeremy Manson Points 81

J'ai écrit la spécification. Le TL; DR version de cette réponse est que juste parce qu'elle peut voir les 0 pour y, cela ne signifie pas qu'il est garanti à voir les 0 y.

Dans ce cas, le champ final spec garantit que vous pourrez voir 3 pour x, comme vous le soulignez. Penser à l'écrivain thread comme ayant 4 instructions:

r1 = <create a new TestClass instance>
r1.x = 3;
r1.y = 4;
f = r1;

La raison pour laquelle vous ne pourriez pas voir 3 pour x est si le compilateur réorganisées ce code:

r1 = <create a new TestClass instance>
f = r1;
r1.x = 3;
r1.y = 4;

La façon la garantie pour la finale champs est généralement mis en œuvre dans la pratique est de s'assurer que le constructeur se termine avant tout ultérieures du programme d'actions lieu. Imaginez que quelqu'un a érigé une grande barrière entre r1.y = 4 et f = r1. Donc, dans la pratique, si vous avez des final les champs d'un objet, vous êtes susceptible d'obtenir de la visibilité pour tous.

Maintenant, en théorie, quelqu'un pourrait écrire un compilateur qui n'est pas mis en œuvre de cette façon. En fait, beaucoup de gens ont souvent parlé de test de code par écrit les plus malveillants compilateur possible. Cette situation est particulièrement fréquente parmi les personnes C++, qui ont beaucoup, beaucoup de undefined dans les coins de leur langue qui peut conduire à de terribles bugs.

7voto

Peter Lawrey Points 229686

À partir de Java 5.0, vous garantit que tous les threads de voir l'état final défini par le constructeur.

Si vous voulez voir ce échouent, vous pouvez essayer un ancien de la JVM comme 1.3.

Je n'aurais pas l'impression de tous les tester, je voudrais imprimer uniquement les échecs. Vous pourriez obtenir un seul échec en millions de dollars, mais les rater. Mais si vous n'imprimez que les échecs, ils devraient être faciles à repérer.

Un moyen plus simple de voir cet échec est à ajouter à l'écrivain.

f.y = 5;

et de test pour

int y = TestClass.f.y; // could see 0, 4 or 5
if (y != 5)
    System.out.println("y = " + y);

5voto

assylias Points 102015

J'aimerais bien voir un test qui échoue ou une explication de pourquoi il n'est pas possible avec l'actuel Jvm.

Le Multithreading et de Test

Vous ne pouvez pas prouver qu'une application multithread est cassé (ou pas) par les tests pour plusieurs raisons:

  • le problème peut apparaître qu'une seule fois toutes les x heures, x étant tellement élevé qu'il est peu probable que vous la voyez dans un court test
  • le problème peut apparaître uniquement avec certaines combinaisons de JVM / architectures de processeur

Dans votre cas, pour faire le test de rupture (c'est à dire à observer y == 0) exigent que le programme pour voir une partie de construit de l'objet où certains champs ont été correctement construits et d'autres pas. Généralement, cela ne se fait pas sur x86 / hotspot.

Comment faire pour déterminer si un code multithread est cassé?

La seule façon de prouver que le code est valide ou cassé est d'appliquer la JLS règles et de voir quels résultats. Avec les données de la course de l'édition (pas de synchronisation autour de la publication de l'objet ou de y), JL n'offre aucune garantie d'y sera vu comme 4 (il pourrait être vu avec sa valeur par défaut de 0).

Peut que le code de vraiment casser?

Dans la pratique, certaines machines virtuelles seront plus à même de faire le test échoue. Par exemple, certains compilateurs (cf "Un cas de test montrant que ça ne fonctionne pas" dans cet article) pourrait transformer l' TestClass.f = new TestClass(); dans quelque chose comme (parce qu'il est publié par une des données de course):

(1) allocate memory
(2) write fields default values (x = 0; y = 0) //always first
(3) write final fields final values (x = 3)    //must happen before publication
(4) publish object                             //TestClass.f = new TestClass();
(5) write non final fields (y = 4)             //has been reodered after (4)

Le JLS mandats (2) et (3) se produire avant que l'objet de publication (4). Toutefois, en raison des données de course, aucune garantie n'est donnée par (5) - il serait effectivement une exécution légale si un thread jamais observé que l'opération d'écriture. Avec le bon thread entrelacement, il est donc concevable que si l' reader tourne entre 4 et 5, vous obtiendrez le résultat souhaité.

Je n'ai pas de symantec JIT à la main de sorte que ne peut pas le prouver expérimentalement :-)

3voto

Dog Points 2592

Ici est un exemple de valeurs par défaut de non final valeurs observées, malgré que le constructeur définit et n'a pas de fuite d' this. C'est basé sur mon autre question qui est un peu plus compliqué. Je continue à voir des gens dire qu'il ne peut pas se produire sur les ordinateurs x86, mais mon exemple qui se passe sur x64 linux openjdk 6...

-1voto

Jakub Zaverka Points 5909

Et vous avez modifié le constructeur pour ce faire:

 public TestClass() {
 Thread.sleep(300);
   x = 3;
   y = 4;
}
 

Je ne suis pas un expert des finales et des initialiseurs JLF, mais le bon sens me dit que cela devrait retarder le réglage de x suffisamment longtemps pour que les écrivains enregistrent une autre valeur?

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