Frage

Bezieht sich auf meine Frühere Frage zu unvollständig konstruierten Objekten, Ich habe eine zweite Frage. Wie Jon Skeet betonte, gibt es am Ende eines Konstruktors eine implizite Gedächtnisbarriere, die dafür sorgt final Felder sind für alle Threads sichtbar. Aber was ist, wenn ein Konstruktor einen anderen Konstruktor nennt? Gibt es eine solche Erinnerungsbarriere am Ende eines jeden von ihnen oder nur am Ende desjenigen, der an erster Stelle gerufen wurde? Das heißt, wenn die "falsche" Lösung lautet:

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });
    }
}

Und die richtige wäre eine Fabrikmethodeversion:

public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        }
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

Würde auch die folgenden Arbeiten funktionieren oder nicht?

public class MyListener {
    private final EventListener listener;

    private MyListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        }
    }

    public MyListener(EventSource source) {
        this();
        source.register(listener);
    }
}

Aktualisieren: Die wesentliche Frage ist, dass dies ist this() garantiert tatsächlich Anruf Der private Konstruktor oben (in diesem Fall würde es die Barriere geben, in der beabsichtigt wird und alles sicher wäre), oder ist es möglich, dass der private Konstruktor bekommt Eingebraucht in die Öffentlichkeit als Optimierung, um eine Speicherbarriere zu sparen (in diesem Fall würde es erst am Ende des öffentlichen Konstruktors eine Barriere geben)?

Sind die Regeln von this() genau irgendwo definiert? Wenn nicht, dann denke ich, dass wir davon ausgehen müssen, dass inlining gekettete Konstrukteure und wahrscheinlich einige JVMs oder vielleicht sogar einige JVMs sind javacS machen es.

War es hilfreich?

Lösung

Ich denke, es ist sicher, da das Java -Speichermodell feststellt, dass:

Lassen Ö Objekt sein und c ein Konstruktor sein für Ö in dem ein endgültiges Feld f ist geschrieben. Eine Einfrieraktion auf dem endgültigen Feld f von Ö findet auf, wann c Ausläuft entweder normalerweise oder abrupt. Beachten Sie, dass wenn ein Konstruktor einen anderen Konstruktor aufruft und der aufgerufene Konstruktor ein endgültiges Feld festlegt, das Einfrieren für das endgültige Feld am Ende des aufgerufenen Konstruktors stattfindet.

Andere Tipps

Ein Objekt wird als vollständig initialisiert, wenn sein Konstruktor beendet ist.

Dies gilt auch für gekettete Konstruktoren.

Wenn Sie sich im Konstruktor registrieren müssen, definieren Sie den Hörer als statische innere Klasse. Das ist sicher.

Ihre zweite Version ist nicht korrekt, da sie den Verweis von "This" zu einer Flucht aus dem Konstruktionsprozess ermöglicht. Mit dieser Flucht wird die Initialisierungssicherheit garantiert, die endgültige Felder ihre Sicherheit bieten.

Um die implizite Frage zu beantworten, erfolgt die Barriere am Ende des Bauwesens nur am Ende der Objektkonstruktion. Die Intuition, die ein Leser über das Inlining angeboten hat, ist nützlich. Aus der Perspektive des Java -Speichermodells existieren keine Methodengrenzen.

BEARBEITEN Nach dem Kommentar, der vorschlug, dass der Compiler den privaten Konstruktor (ich hatte nicht an diese Optimierung gedacht), besteht die Wahrscheinlichkeit, dass der Code unsicher ist. Und der schlimmste Teil des unsicheren Multithread -Codes ist, dass es scheint, dass es zu funktioniert, sodass Sie ihn besser vermeiden möchten. Wenn Sie unterschiedliche Tricks spielen möchten (Sie möchten die Fabrik aus irgendeinem Grund wirklich vermeiden), sollten Sie einen Wrapper hinzufügen, um die Kohärenz von Daten im internen Implementierungsobjekt zu gewährleisten und sich im externen Objekt zu registrieren.


Ich vermute, dass es zerbrechlich sein wird, aber OK. Der Compiler kann nicht wissen, ob der interne Konstruktor nur aus anderen Konstruktoren aufgerufen wird oder nicht. Daher muss sichergestellt werden, dass das Ergebnis für den Code, das nur den internen Konstruktor aufruft dort an Ort und Stelle sein.

Ich würde vermuten, dass der Compiler die Speicherbarriere am Ende jedes einzelnen Konstruktors hinzufügen würde. Das Problem ist immer noch da: Sie passieren die this Verweis auf andere Code (möglicherweise andere Threads), bevor er vollständig konstruiert ist-das ist schlecht-, aber wenn der einzige "Aufbau", der übrig bleibt, registriert, ist der Objektzustand so stabil wie jemals zuvor.

Die Lösung ist zerbrechlich In diesem Tag müssen Sie oder ein anderer Programmierer möglicherweise ein anderes Mitglied zum Objekt hinzufügen und vergessen, dass die geketteten Konstrukteure ein Parallelitätstrick sind und sich möglicherweise entscheiden, das Feld im öffentlichen Konstruktor zu initialisieren, und dabei eine hinzufügen Es ist schwer, potenzielles Datenrennen in Ihrer Anwendung zu erkennen, daher würde ich versuchen, dieses Konstrukt zu vermeiden.

Übrigens: Die erratene Sicherheit kann falsch sein. Ich weiß nicht, wie komplex/intelligent der Compiler ist und ob die Speicherbarriere (oder dergleichen) etwas ist, das er optimieren kann ... da der Konstruktor privat ist, hat der Compiler über genügend Informationen, um zu wissen, dass es ist Nur aus anderen Konstruktoren genannt, und das sind genügend Informationen, um festzustellen, dass der Synchronisationsmechanismus im internen Konstruktor nicht erforderlich ist ...

Die Fluchtobjektreferenz in C-TOR kann ein unvollständig konstruiertes Objekt veröffentlichen. Das ist sogar wahr Wenn die Veröffentlichung die letzte Aussage im Konstruktor ist.

Ihr Safelistener verhalten sich in einer gleichzeitigen Umgebung möglicherweise nicht in Ordnung, auch wenn eine C-Tor-Inlining durchgeführt wird (was meiner Meinung nach nicht der Fall ist-, denken Sie daran, Objekte mit Reflexion zu erstellen, indem Sie auf private C-Tor zugreifen).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top