Double-Checked Locking

S

SlaterB

Gast
hallo,

ich finde den Artikel
The "Double-Checked Locking is Broken" Declaration
nicht genau genug (edit: bzw. möchte nicht alle referenzierten Links auch noch lesen ;) ),

ich kann mir gut vorstellen dass er richtig ist, wäre mit deutlicheren Erklärungen aber noch zufriedener,
es geht um 'lazy initialization in a multithreaded environment', also mit Synchronisation

die erste ernstzunehmende Version
Java:
class Foo {
    private Helper helper = null;

    public Helper getHelper() {
        if (this.helper == null) {
            synchronized (this) {
                if (this.helper == null) {
                    this.helper = new Helper();
                }
            }
        }
        return this.helper;
    }
}
wird abgelehnt mit (nach meinem Verständnis) der Begründung, dass die Variable helper schon gesetzt wird,
bevor der Konstruktor von Helper komplett ausgeführt wird,

nach welchen internen Optimierungsgründen auch immer, das finde ich nicht schön, kann ich aber halbwegs akzeptieren,
da es aus lokaler Sichtweise hinkommt, bevor die nächste Code-Zeile drankommt wird schon alles fertig sein,
Reihenfolge wäre für nur einen Thread relativ egal

-----

das folgende Beispiel im Artikel ist mir dann schon bisschen zu kompliziert,
enthält nebenher auch langweilige doppelte Synchronisation,

der spannendere Teil ist für mich die Nutzung einer lokalen Variable, ein verkürzter Code:
Java:
class Foo {
    private Helper helper = null;

    public Helper getHelper() {
        if (this.helper == null) {
            synchronized (this) {
                if (this.helper == null) {
                    Helper h = new Helper();
                    this.helper = h;
                }
            }
        }
        return helper;
    }
}
die ablehnende Begründung im Artikel kümmert sich mehr um die doppelte Synchronisation usw.,
ich frage mich aber ob nicht allein die lokale Variable hilft, das Problem des ersten Beispiels zu beheben?
kann nicht in Zeile 9 davon ausgegangen werden dass das Objekt h komplett initialisiert ist?
denn wenn nicht, dann hätte man ja bei jedem lokal erzeugten Objekt das Problem, dass es in der nächsten Zeile vielleicht noch nicht sauber erstellt ist..,

in speziell diesem Fall kann man vielleicht wieder böse Optimierungen vermuten, etwa dass die lokale Variable h komplett rausfällt
und der Code exakt zur Variante 1 'verbessert' wird, ist das noch das verbleibende Problem?

aber man kann zwischen Zeile 8 und 9 ja noch mehr Code einfügen, z.B. eine Ausgabe der Attribute oder hashCode()-Aufruf,
danach richten sich doch hoffentlich die denkbaren Befehlsvertauschungen?

strenggenommen wäre dann denkbar, dass der Code immer noch zu
Java:
this.helper = new Helper();
// Ausgaben oder irgendein Aufruf an this.helper
optimiert wird?,
mit den gleichen Problemen für andere Threads am Anfang mit uninitialisierten this.helper
aber durchaus funktionierenden Ausgaben später

-----

dann würde ich weiterdenken:
Hilfsmethoden an sich helfen vielleicht nicht, immer mit dem Totschlagargument dass deren Code einfach herüberkopiert wird..,
eine Initialisierungsklasse wäre ziemlich heftig, mit Konstruktor und get-Methode, das kann doch kaum alles herausgezogen werden,
da ich aber danach noch andere Ideen hatte erspare ich erst mal den langen Code mit Erklärungen dazu ;)

zurück nur zur Klasse Foo, nur getHelper dort, in synchronized im if zu den entscheidenen Zeilen:

Java:
this.helper = new Helper();
soll schlecht sein

Java:
Helper h = new Helper();
this.helper = h;
soll auch schlecht sein

wie steht es mit folgendem:
Java:
this.helper = new Helper().returnYourself();
mit einer Methode returnYourself() die nur sich selbst, also this zurückgibt,
kann dieser eigentlich sinnlose Methodenaufruf wegoptimiert werden?
wenn nicht, kann die Methode ausgeführt werden bevor das Helper-Objekt vollständig initialisiert ist?

und hoffentlich steht doch wohl in der Variable this.helper nichts drin, bevor nicht die Methode returnYourself() ausgeführt wurde?


oder noch eine Variante:
Java:
Helper h = new Helper();
this.helper = h.hashCode() < Integer.MAX_VALUE ? h : null;
mal abgesehen von der Frage ob der Hashcode vielleicht doch Integer.MAX_VALUE ist,
ist es irgendwie denkbar dass die Methode hashCode() vor kompletter Initialisierung von h ausgeführt wird?
oder auf andere Weise der ganze Block übersprungen wird?


-------

dann noch die Möglichkeit, das Objekt nicht direkt abzulegen sondern in einer List/ Map zu speichern,
Java wird doch wohl nicht wagen, dorthin ein uninitialisiertes Objekt zu übergeben?

warum wird im Artikel soweit ich das sehe auf derartiges nicht eingegangen?
die Varianten mit ThreadLocal oder Guard<LOCK> scheinen mir höchst kompliziert,
als wäre generell nichts möglich und nur zum Spaß werden akademische Versionen gezeigt

volatile und Immutable sind am Ende immerhin noch saubere Lösungen
 
Zuletzt bearbeitet von einem Moderator:

faetzminator

Gesperrter Benutzer
Ich hab nicht alles durchgeschaut, aber nicht eher
Java:
class Foo {
    private Helper helper = null;
 
    public Helper getHelper() {
        if (this.helper == null) {
            synchronized (this) {
                Helper h = new Helper();
                if (this.helper == null) {
                    this.helper = h;
                }
            }
        }
        return helper;
    }
}
?
 

Marco13

Top Contributor
Den Artikel hatte ich auch mal ... "gelesen". Aber es stimmt: Besonders genau ist er nicht, und wenn man die "entmutigenden" ersten Worte liest, und dann so drüberscrollt, und die Überschriften liest:
It does not work
Lorem ipsum...
It really does not work
Dolor sit amet...
It really does never work
Lorem ipsum...
It really does not work and will never work, get over it
Dolor sit amet...

How it works, anyhow:
Code:
...
Ist die Versuchung schon groß, das einfach zu akzeptieren und den Code am Ende zu verwenden :D

Detaillierte Begründungen, quasi der komplette "Erklär-Baum" jedes einzelnen Aspekts oder vermeintlichen Workarounds bis runter zu den "Blättern" (einzelnen Zeilen und Paragraphen in der JVM Specification) wären mir dann auch zu viel.

kann nicht in Zeile 9 davon ausgegangen werden dass das Objekt h komplett initialisiert ist?
denn wenn nicht, dann hätte man ja bei jedem lokal erzeugten Objekt das Problem, dass es in der nächsten Zeile vielleicht noch nicht sauber erstellt ist..,
...

Meinem Verständnis nach trifft das zu, was du beschrieben hast: Einerseits könnte die lokale Variable vermutlich rausfallen. Zusätzlich können wohl die Anweisungen (d.h. das Initialisieren der Fields im Konstruktor und das Zuweisen des neuen Objektes an die Hilfsvariable, und deren Zuweisung ans Field(!)) beliebig umsortiert werden.

Die Sache mit dem
[c]this.helper = new Helper().returnYourself();[/c]
ist aber schon ein interessanter Gedanke. Da man nicht weiß, WAS der Compiler oder JIT genau optimiert, kann man da eigentlich nur drüber spekulieren. Theoretisch(!) könnte er die Semantik dort erkennen, und theoretisch könnte er es
[c]this.helper = new Helper();[/c]
zusammenschrumpfen, aber erstens ist das praktisch kaum machbar (weil in der "returnYourself" ja auch mehr stehen könnte, als das 'return') und zweitens wäre so eine Optimierung, die das Verhalten (auch in bezug auf SO einen speziellen und heiklen Punkt) ändern würde ziemlich fragwürdig. Noch extremer wäre das mit dem HashCode, was beides plakativ formuliert ja das Ziel erreichen soll, was man als
Code:
Helper h = new Helper();
while (!h.isCompletelyInitialized()) { / * Do nothing */ }
this.helper = h;
schreiben könnte (und was nichtmal unbedingt Pseudocode sein soll, weil es ja den gleichen Effekt erzielen könnte...)

Ab einem bestimmten Punkt drängt sich da schon die Frage auf, welche Krämpfe man sich antun will, und die Frage (bzw. nicht so sehr die eigentlich Frage, sondern die Kette der sich aus möglichen Workarounds ergebenden Fragen) erscheint irgendwann akademisch. Aber ... es wäre schon gut, es genauer zu wissen.
Bin mal gespannt ob noch jemand weniger Spekulationen als mehr konkrete Erleuchtung bringt....:rtfm:
 
S

SlaterB

Gast
danke schonmal, und wie Otto so sagt:
einen hab ich noch, vielleicht am gebräuchlichsten (wer will schon returnYourself() in vielen Klassen)

Java:
this.helper = RealHelper.initialize(new Helper());
mit nur einer zentralen statischen Hilfsmethode, die dann nun richtig ernst rangehen darf, ob Hashcode, Random, Reflection, Einfügen in Liste + Herausholen oder was auch immer,
Aufwand halbwegs egal, hauptsache am Ende wird dasselbe Objekt zurückgegeben
 
Zuletzt bearbeitet von einem Moderator:

Lumaraf

Bekanntes Mitglied
Die Sache mit dem
[c]this.helper = new Helper().returnYourself();[/c]
ist aber schon ein interessanter Gedanke. Da man nicht weiß, WAS der Compiler oder JIT genau optimiert, kann man da eigentlich nur drüber spekulieren. Theoretisch(!) könnte er die Semantik dort erkennen, und theoretisch könnte er es
[c]this.helper = new Helper();[/c]
zusammenschrumpfen, aber erstens ist das praktisch kaum machbar (weil in der "returnYourself" ja auch mehr stehen könnte, als das 'return') und zweitens wäre so eine Optimierung, die das Verhalten (auch in bezug auf SO einen speziellen und heiklen Punkt) ändern würde ziemlich fragwürdig. Noch extremer wäre das mit dem HashCode, was beides plakativ formuliert ja das Ziel erreichen soll, was man als

Java 7 kann diese Optimierungen soweit ich weiß vornehmen. Unter Java 6 bin ich mir nicht sicher ob der Fall da schon abgedeckt wird. Dazu kommt noch das die CPU sich an der Ausführungsreihenfolge auch noch austobt. Die Reihenfolge kann sich sogar während die JVM läuft ändern.

Im Endeffekt hat man nur eine der folgenden Möglichkeiten:
1. auf Double-Checked Locking verzichten und sich auf die lazy Initialisierung der Klassen verlassen
2. das Instanz-Feld volatile deklarieren
3. die Instanz in eine AtomicReference kapseln

Die erste Möglichkeit ist allen anderen überlegen da sie ja keine Überprüfung machen muß ob die Instanz schon erzeugt wurde, die Instanz wird aber trotzdem erst erzeugt wenn man sie das erste mal benötigt.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
JAnruVA Datentypen Berechneten String-Wert in Double umwandeln um weiter zu rechnen Allgemeine Java-Themen 7
Neumi5694 double Vergleich Allgemeine Java-Themen 19
G Modulo - double Allgemeine Java-Themen 21
ReinerCoder Eclipse Fehlermeldung - The constructor Double(double) is deprecated Allgemeine Java-Themen 8
O Variablen Addition von Double-Werten ergibt seltsame 0.9999999 Zahlen Allgemeine Java-Themen 2
B Long in einen Double umwandeln und im Label anzeigen Allgemeine Java-Themen 7
VfL_Freak Double mit zwei festen NK-Stellen ausgeben Allgemeine Java-Themen 9
B List<Integer> ist List<Double> ? Allgemeine Java-Themen 6
SkyScreamer Variablen Int umwande in Double/ 1 Miner = 5 Cookies/s Allgemeine Java-Themen 2
T Teile eines Double-Wertes verändern Allgemeine Java-Themen 2
T Double salted bcrypt Allgemeine Java-Themen 0
A Zahl abgerundet obwohl Double Allgemeine Java-Themen 9
R jTable, nur Werte zwischen 2 Double values ausgeben Allgemeine Java-Themen 3
Y String to Double ohne ParseDouble Allgemeine Java-Themen 11
D Double aus String auslesen Allgemeine Java-Themen 8
M double Array Allgemeine Java-Themen 8
P Wird double und float auf jedem Computer gleich berechnet? Allgemeine Java-Themen 10
C String in double[][] konvertieren! Allgemeine Java-Themen 5
M Cast double[]-->Object[] oder Vector<double[]> Allgemeine Java-Themen 3
Zettelkasten Double --> Integer Allgemeine Java-Themen 4
M float, double, BigDecimal Allgemeine Java-Themen 5
S Variablen Prüfen ob Number vom Typ Integer, Float, Double, ... ist Allgemeine Java-Themen 2
J String mit "," in Double Wert konvertieren Allgemeine Java-Themen 7
R Eclipse Verschiedene Ergebnisse bei Berechnung eines double-Werts Allgemeine Java-Themen 5
B Variablen Bug of Float. & Double.MIN_VALUE! Allgemeine Java-Themen 4
T ArrayList<double[]> persitent anlegen Allgemeine Java-Themen 7
K double Zufallszahl Allgemeine Java-Themen 6
S String in Double - letztes Zeichen fehlt Allgemeine Java-Themen 4
T int <-> integer, double <-> Double... Allgemeine Java-Themen 2
J Umstellung von double auf BigDecimal Allgemeine Java-Themen 5
H2SO3- String 1.000,00 in double umwandeln Allgemeine Java-Themen 12
I 2D-Grafik Polygon mit Double Werten zeichnen Allgemeine Java-Themen 4
M FIFO Queue: bytes in, float/double/etc out Allgemeine Java-Themen 5
H double dispatch und equals(Object) Allgemeine Java-Themen 6
vandread Float/Double probleme bei der Multiplikation?! Allgemeine Java-Themen 3
J Casting Problem Object, Double und String Allgemeine Java-Themen 3
F double auf 2 Nachkommastellen runden Allgemeine Java-Themen 9
D Double to Integer - ist das möglich? Allgemeine Java-Themen 3
T Object auf Double, Int, String testen Allgemeine Java-Themen 5
H2SO3- großen double in string mit e umwandeln Allgemeine Java-Themen 4
T Problem: Double als Geldbetrag - Rundungsfehler Allgemeine Java-Themen 5
P große double Zahlen und modulo Allgemeine Java-Themen 8
G Double Wert über OutputStream verschicken Allgemeine Java-Themen 2
M größer als double? Allgemeine Java-Themen 4
N Double ohne Wissenschaftliche Schreibweiße Allgemeine Java-Themen 5
M Double Braces Notation um Collections zu initialisieren Allgemeine Java-Themen 9
R Eigene ArrayList vom Typ Short, Integer oder Double Allgemeine Java-Themen 4
R Double Werte aus byte[] auslesen Allgemeine Java-Themen 5
T XStream.toXML => double mit , anstelle . Allgemeine Java-Themen 3
G Fehler bei Array.getDouble mit Double[] als Argument! Allgemeine Java-Themen 6
A Problem bei Übergabe von Werten in ein double Array. Allgemeine Java-Themen 21
M Double immer mit 2 Kommastellen Allgemeine Java-Themen 3
G double in Date Allgemeine Java-Themen 5
reibi double-Wert auf 2 Stellen nach dem Komma abschneiden Allgemeine Java-Themen 6
G Genauigkeit von Double Allgemeine Java-Themen 2
F Double mit Streams aus Datei einlesen Allgemeine Java-Themen 3
V "double in Exponentialschreibweise" normal ausgebe Allgemeine Java-Themen 2
S List<Double> oder Double[] in double[] zu konvertieren Allgemeine Java-Themen 6
C casten vom Typ Object nach Double[][] Allgemeine Java-Themen 2
E int vs. float vs. double Allgemeine Java-Themen 7
T cast Object to Double[] Allgemeine Java-Themen 2
DEvent double von c nach Java Allgemeine Java-Themen 4
K Double-Zahl runden Allgemeine Java-Themen 4
J double 3.0 soll ohne Nachkommastelle ausgegeben werden Allgemeine Java-Themen 12
S Java Double addieren Allgemeine Java-Themen 3
A string zu double Allgemeine Java-Themen 3
T double to object Allgemeine Java-Themen 3
J Große Zahl (double) as text ausgeben? Allgemeine Java-Themen 2
G Verständnisproblem double und float Allgemeine Java-Themen 7
padde479 String in double parsen Allgemeine Java-Themen 6
E double auf int runden, nicht abschneiden Allgemeine Java-Themen 2
G Double Zahl quadrieren Allgemeine Java-Themen 8
C double Zahlen mit drei NachkommaStellen in String umwandeln Allgemeine Java-Themen 2
T Nachkommastellen double Allgemeine Java-Themen 18
M double aufrunden Allgemeine Java-Themen 6
W Division mit float und double Allgemeine Java-Themen 2
D JTextfield für double werte Allgemeine Java-Themen 2
S Format von Double Allgemeine Java-Themen 9
F Umwandlungsproblem mit double Allgemeine Java-Themen 2
S Problem! Lösung mit Double buffering Allgemeine Java-Themen 3
T Vector <-> double Allgemeine Java-Themen 4
W float/double verhält sich seltsam Allgemeine Java-Themen 6
D Double: Grenzen und Darstellung Allgemeine Java-Themen 7
W String to Double (landesspezifisch) Allgemeine Java-Themen 14
T Fließkomma (double) richtig runden Allgemeine Java-Themen 7
P Class zu Integer, String, Double etc. Allgemeine Java-Themen 11
R Fehler in WindowsXP oder Java? - double in int konvertieren Allgemeine Java-Themen 10
E NumberFormatException bei cast auf double Allgemeine Java-Themen 5
D Wann ist das ergebnis einer Rechnung eine Double? Allgemeine Java-Themen 7
G Double Zahl auf 4 Stellen hinter Komma kuerzen Allgemeine Java-Themen 4
G warum operator || cannot be applied to int, double Allgemeine Java-Themen 11
A Probleme Umwandlung Ausdruck String in double Allgemeine Java-Themen 4
B Double-Wert aus einem HexString erstellen. Allgemeine Java-Themen 11
R Double Buffering zu langsam Allgemeine Java-Themen 11
C Bug in Java1.4.2_03 mit double Allgemeine Java-Themen 5
H Double mit 2 Nachkommastellen speichern. Allgemeine Java-Themen 5
C Double zu Hex oder Byte Allgemeine Java-Themen 4
V Event Handling ActionListener nur bei "checked" CheckBoxMenuItem Allgemeine Java-Themen 2
M Spring Locking Allgemeine Java-Themen 13
M Spring Locking Allgemeine Java-Themen 6

Ähnliche Java Themen

Neue Themen


Oben