Discussion:
Frage bzgl. Lazy Creation Singleton
(zu alt für eine Antwort)
Divad
2009-02-16 13:51:21 UTC
Permalink
Hallo zusammen,

ich bin auf mehreren Seiten darauf gestoßen, dass bei der Erzeugung von Lazy
Creation-Singletons die Performance-Einbußen durch die dauernde Synchronisation
von Threads in Java nicht zu umgehen sei.

Allerdings stellt sich mir die Frage, warum dies nicht wie folgt klappen soll:

/* Die einzige Instanz */
private static LazyCreationSingleton LC_INSTANCE;

/* Original-Initialisierung mit Performanceeinbuße */
public synchronized static LazyCreationSingleton getInstance() {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
}
return LC_INSTANCE;
}

/* Nun die Änderungen */
private static boolean isInit = false;
private synchronized static LazyCreationSingleton initialize() {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
isInit = true;
}
return LC_INSTANCE;
}
public static LazyCreationSingleton getInstance() {
return (isInit) ? LC_INSTANCE : initialize();
}

Ich habe es oberflächlich getestet und da führte es zu keinen Exceptions.
Solange isInit false liefert, rufen alle Threads die synchronisierte Methode
initialize() auf. Sobald true, kann die Einzel-Instanz direkt zurückgegeben
werden.

Falls vorhanden, wo liegt die Zwickmühle?
Bernd Hohmann
2009-02-16 14:14:01 UTC
Permalink
Post by Divad
ich bin auf mehreren Seiten darauf gestoßen, dass bei der Erzeugung von Lazy
Creation-Singletons die Performance-Einbußen durch die dauernde Synchronisation
von Threads in Java nicht zu umgehen sei.
Weil Du damit einen verdeckten Doublecheck-Lock über eine Hilfsvariable
gebaut hast. Real machst Du in Deinem Beispiel nämlich sowas bzw. kann
darauf umgebaut werden:

public static LazyCreationSingleton getInstance() {
if (LC_INSTANCE == null) {
synchronized(this) {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
}
}
return LC_INSTANCE;
}


Abhilfe gibt es viele. zb. LC_INSTANCE als Volatile zu kennzeichnen (ab
Java 5) oder schlichtweg das Singleton hochzuziehen ehe es multithreaded
wird (so mach ich das).

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Divad
2009-02-16 15:00:32 UTC
Permalink
Post by Bernd Hohmann
Post by Divad
Allerdings stellt sich mir die Frage, warum dies nicht wie folgt klappe
Weil Du damit einen verdeckten Doublecheck-Lock über eine Hilfsvariable
gebaut hast. Real machst Du in Deinem Beispiel nämlich sowas bzw. kann
public static LazyCreationSingleton getInstance() {
if (LC_INSTANCE == null) {
synchronized(this) {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
}
}
return LC_INSTANCE;
}
Abhilfe gibt es viele. zb. LC_INSTANCE als Volatile zu kennzeichnen (ab
Java 5) oder schlichtweg das Singleton hochzuziehen ehe es multithreaded
wird (so mach ich das).
Das es andere Wege gibt, war mir schon bewusst. An vielen Stellen wird im
Übrigen der Weg über enum als "Best Practice" angeführt:

public static enum EnumSingleton {
SINGLETON;
}
Jochen Theodorou
2009-02-16 15:47:01 UTC
Permalink
Divad schrieb:
[...]
Post by Divad
Das es andere Wege gibt, war mir schon bewusst. An vielen Stellen wird im
public static enum EnumSingleton {
SINGLETON;
}
der Gedanke hierbei ist folgender:

ein

class Foo {
public final static Foo INSTANCE;
static {
INSTANCE = new Foo();
}
}

funktioniert garantiert... das sichert dir die JVM. Das ist aber nicht
lazy. Bei einem

public static enum EnumSingleton {
SINGLETON;
}

ist SINGLETON eine eigene Klasse, es wird also erst initialisiert, wenn
ich tatsächlich darauf zugreife, entsprechend wird der statische
Konstruktor hier nur dann ausgeführt, und einmalig. Allerdings darfst du
dann die Singleton-Klasse niemals direkt irgendwo außer in dem Enum
benutzen.

Gruss theo
Patrick Roemer
2009-02-16 22:11:30 UTC
Permalink
Post by Jochen Theodorou
class Foo {
public final static Foo INSTANCE;
static {
INSTANCE = new Foo();
}
}
funktioniert garantiert... das sichert dir die JVM. Das ist aber nicht
lazy. Bei einem
public static enum EnumSingleton {
SINGLETON;
}
Warum static? In welcher umgebenden Klasse?
Post by Jochen Theodorou
ist SINGLETON eine eigene Klasse, es wird also erst initialisiert, wenn
ich tatsächlich darauf zugreife, entsprechend wird der statische
Konstruktor hier nur dann ausgeführt, und einmalig.
In beiden Faellen wird doch bei (auch nur statischer) erster Verwendung
der "umgebenden" Klasse (Foo/EnumSingleton) in deren statischen
Initializer ein Exemplar erzeugt (Foo/EnumSingleton$1). Wo ist der
Unterschied?

Viele Gruesse,
Patrick
Jochen Theodorou
2009-02-16 23:48:28 UTC
Permalink
Post by Patrick Roemer
Post by Jochen Theodorou
class Foo {
public final static Foo INSTANCE;
static {
INSTANCE = new Foo();
}
}
funktioniert garantiert... das sichert dir die JVM. Das ist aber nicht
lazy. Bei einem
public static enum EnumSingleton {
SINGLETON;
}
Warum static? In welcher umgebenden Klasse?
ist aus versehen reingerutscht
Post by Patrick Roemer
Post by Jochen Theodorou
ist SINGLETON eine eigene Klasse, es wird also erst initialisiert, wenn
ich tatsächlich darauf zugreife, entsprechend wird der statische
Konstruktor hier nur dann ausgeführt, und einmalig.
In beiden Faellen wird doch bei (auch nur statischer) erster Verwendung
der "umgebenden" Klasse (Foo/EnumSingleton) in deren statischen
Initializer ein Exemplar erzeugt (Foo/EnumSingleton$1). Wo ist der
Unterschied?
das habe ich ein wenig falsch gemacht, aber so wie ich es meinte wird es
nirgends erwähnt.. da habe ich was falsch im Gedächtnis gehabt...

Gruss theo
Divad
2009-02-16 15:11:38 UTC
Permalink
Post by Bernd Hohmann
Post by Divad
Allerdings stellt sich mir die Frage, warum dies nicht wie folgt klappe
Weil Du damit einen verdeckten Doublecheck-Lock über eine Hilfsvariable
gebaut hast. Real machst Du in Deinem Beispiel nämlich sowas bzw. kann
public static LazyCreationSingleton getInstance() {
if (LC_INSTANCE == null) {
synchronized(this) {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
}
}
return LC_INSTANCE;
}
Da hängt einiges davon ab wie der Compiler die Sache handhabt. Ich habe auf die
Schnelle nur etwas zu C++ gefunden:
http://www.devarticles.com/c/a/Cplusplus/C-plus-in-Theory-Why-the-Double-Check-
Lock-Pattern-Isnt-100-ThreadSafe/3/

Wenn der Java-Compiler auch so arbeitet (Erzeugung des Objekts und Referenz
setzen in unbestimmter Reihenfolge), dann verstehe ich, warum obiges zum Problem
werden kann.
Bei der Lösung mit der Hilfsvariable sieht es meines Erachtens jedoch anders
aus, trotzdessen sich ähnlich vom Ablauf her ansieht. Hier wird der
Wahrheitswert doch erst nach Erstellung UND der Referenzierung gesetzt oder
täusche ich mich damit?
Post by Bernd Hohmann
Abhilfe gibt es viele. zb. LC_INSTANCE als Volatile zu kennzeichnen (ab
Java 5) oder schlichtweg das Singleton hochzuziehen ehe es multithreaded
wird (so mach ich das).
Das es andere Wege gibt, war mir schon bewusst. An vielen Stellen wird im
Übrigen der Weg über enum als "Best Practice" angeführt:

public static enum EnumSingleton {
SINGLETON;
}
Bernd Hohmann
2009-02-16 15:38:09 UTC
Permalink
Post by Divad
Wenn der Java-Compiler auch so arbeitet (Erzeugung des Objekts und Referenz
setzen in unbestimmter Reihenfolge), dann verstehe ich, warum obiges zum Problem
werden kann.
Macht er.
Post by Divad
Bei der Lösung mit der Hilfsvariable sieht es meines Erachtens jedoch anders
aus, trotzdessen sich ähnlich vom Ablauf her ansieht. Hier wird der
Wahrheitswert doch erst nach Erstellung UND der Referenzierung gesetzt oder
täusche ich mich damit?
Gesetzt schon, aber es ist nicht sichergestellt dass ein weiterer Thread
der da einsteigt die Änderung schon sieht. Hilfsweise setzt man die
Marker-Variable auf "volatile", dann hat man eher eine Chance.

Ich sag das deshalb so vorsichtig, weil selbst volatile immer mal (durch
Bugs in der Runtime) nicht die Hoffnungen erfüllt die man darin setzt.
Post by Divad
Post by Bernd Hohmann
Abhilfe gibt es viele. zb. LC_INSTANCE als Volatile zu kennzeichnen (ab
Java 5) oder schlichtweg das Singleton hochzuziehen ehe es multithreaded
wird (so mach ich das).
Das es andere Wege gibt, war mir schon bewusst. An vielen Stellen wird im
Es geht nicht um andere Wege sondern dass Singletons eigentlich nur eine
Notlösung sind und lazy init im Multithreading Dir im unpassenden Moment
(nämlich wenn das Projekt das erste Mal im Echtbetrieb unter Last läuft)
massive Knüppel zwischen die Beine wirft.

Denn wenn Dir das SYNCHRONIZED beim hochziehen des Singletons das Timing
schon versaut, dann ist der Thread so zeitkritisch dass man auf solche
Experimente verzichten sollte.

In diesem Fall sorge ich durch eintsprechendes Locking dafür, dass run()
beim Hochfahren im Gänsemarsch durchlaufen wird und hole mir das ganz am
Anfang im run().

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Jochen Theodorou
2009-02-16 15:51:30 UTC
Permalink
Post by Bernd Hohmann
Post by Divad
Wenn der Java-Compiler auch so arbeitet (Erzeugung des Objekts und
Referenz setzen in unbestimmter Reihenfolge), dann verstehe ich, warum
obiges zum Problem werden kann.
Macht er.
Post by Divad
Bei der Lösung mit der Hilfsvariable sieht es meines Erachtens jedoch
anders aus, trotzdessen sich ähnlich vom Ablauf her ansieht. Hier wird
der Wahrheitswert doch erst nach Erstellung UND der Referenzierung
gesetzt oder täusche ich mich damit?
Gesetzt schon, aber es ist nicht sichergestellt dass ein weiterer Thread
der da einsteigt die Änderung schon sieht. Hilfsweise setzt man die
Marker-Variable auf "volatile", dann hat man eher eine Chance.
Ich sag das deshalb so vorsichtig, weil selbst volatile immer mal (durch
Bugs in der Runtime) nicht die Hoffnungen erfüllt die man darin setzt.
und selbst wenn isInit volatile ist, dann ist es doch LC_INSTANCE noch
lange nicht. Ein thread kann dann noch immer was falsches sehen.... zum
Beispiel null!

Gruss theo
Bernd Hohmann
2009-02-16 17:36:30 UTC
Permalink
Post by Jochen Theodorou
Post by Bernd Hohmann
Ich sag das deshalb so vorsichtig, weil selbst volatile immer mal (durch
Bugs in der Runtime) nicht die Hoffnungen erfüllt die man darin setzt.
und selbst wenn isInit volatile ist, dann ist es doch LC_INSTANCE noch
lange nicht. Ein thread kann dann noch immer was falsches sehen.... zum
Beispiel null!
Nach dem verlassen des SYNCHRONIZED und innerhalb aber schon, da muss
alles egal wie abgearbeitet sein was im Block selber läuft (jedenfalls
verlass ich mich da immer drauf).

Desterwegen ja die Idee des OP mit dem double-lock: wenn isInit (warum
auch immer) noch auf "false" steckt, dann läuft man halt nochmal
"fälschlicherweise" ins SYNCHRONIZED rein und merkt es da erst. Nach
paar Zyklen ist isInit hinreichend stabil sodass man nicht mehr ins
SYNCHRONIZED läuft.

Oder anders herum: Das Ergebnis ist erst nach ner Weile "zuverlässig".

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Jochen Theodorou
2009-02-16 18:22:43 UTC
Permalink
Post by Bernd Hohmann
Post by Jochen Theodorou
Post by Bernd Hohmann
Ich sag das deshalb so vorsichtig, weil selbst volatile immer mal
(durch Bugs in der Runtime) nicht die Hoffnungen erfüllt die man
darin setzt.
und selbst wenn isInit volatile ist, dann ist es doch LC_INSTANCE noch
lange nicht. Ein thread kann dann noch immer was falsches sehen....
zum Beispiel null!
Nach dem verlassen des SYNCHRONIZED und innerhalb aber schon, da muss
alles egal wie abgearbeitet sein was im Block selber läuft (jedenfalls
verlass ich mich da immer drauf).
innerhalb besteht die Gefahr dass jemand in einem anderen Thread was
falsches liest. Und nach dem verlassen des synchronized block... ist es
da wirklich sichergestellt, dass alle Threads für isInit und LC_INSTANCE
die korrektenWerte haben? Weil entweder war ich zu blind die
entsprechende Stelle in der Spezifikation zu finden... oder es ist nicht
der Fall... was allerdings übel wäre, weil ich das durchaus so annehme.
Wenn ich es einfach überlesen habe wäre ich dankbar wenn mir jemand die
genaue Stelle sagen könnte.
Post by Bernd Hohmann
Desterwegen ja die Idee des OP mit dem double-lock: wenn isInit (warum
auch immer) noch auf "false" steckt, dann läuft man halt nochmal
"fälschlicherweise" ins SYNCHRONIZED rein und merkt es da erst. Nach
paar Zyklen ist isInit hinreichend stabil sodass man nicht mehr ins
SYNCHRONIZED läuft.
stimmt, der Konstruktor wird nur einmal ausgeführt.

Gruss theo
Bernd Hohmann
2009-02-16 18:44:57 UTC
Permalink
[...] ist es da wirklich sichergestellt, dass alle Threads für isInit
und LC_INSTANCE die korrektenWerte haben?
Ja schon, sonst macht das synchronize nur zur Hälfte Sinn.
Weil entweder war ich zu blind die entsprechende Stelle in der
Spezifikation zu finden... oder es ist nicht der Fall... was
allerdings übel wäre, weil ich das durchaus so annehme.
Ich hab jetzt 10min durch die SUN Java Seite geklickt und ständig nur im
Kreis gelaufen. Finde also die Specs nicht.

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Jochen Theodorou
2009-02-16 19:03:40 UTC
Permalink
Post by Bernd Hohmann
[...] ist es da wirklich sichergestellt, dass alle Threads für isInit
und LC_INSTANCE die korrektenWerte haben?
Ja schon, sonst macht das synchronize nur zur Hälfte Sinn.
Weil entweder war ich zu blind die entsprechende Stelle in der
Spezifikation zu finden... oder es ist nicht der Fall... was
allerdings übel wäre, weil ich das durchaus so annehme.
Ich hab jetzt 10min durch die SUN Java Seite geklickt und ständig nur im
Kreis gelaufen. Finde also die Specs nicht.
ist doch das hier:
http://java.sun.com/docs/books/jls/third_edition/html/memory.html odeR?

Gruss theo
Bernd Hohmann
2009-02-16 19:47:51 UTC
Permalink
Post by Jochen Theodorou
http://java.sun.com/docs/books/jls/third_edition/html/memory.html odeR?
So von den Worten her würde ich sagen "könnte passen".

Da ich aber Landprogrammierer und kein Informatiker bin, würde ich das
jetzt an den Kollegen Michael Paap weitergeben - der kann darin bestimmt
eher was sinniges Herausziehen als ich.

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Jochen Theodorou
2009-02-16 20:16:10 UTC
Permalink
Post by Bernd Hohmann
Post by Jochen Theodorou
http://java.sun.com/docs/books/jls/third_edition/html/memory.html odeR?
So von den Worten her würde ich sagen "könnte passen".
Da ich aber Landprogrammierer und kein Informatiker bin, würde ich das
jetzt an den Kollegen Michael Paap weitergeben - der kann darin bestimmt
eher was sinniges Herausziehen als ich.
naja, anfangen kann ich damit schon auch was... allerdings weiss ich von
ConcurrentHashMap, dass es wohl eine Technik gibt mittels einer Memory
Barrier dafür zu sorgen dass Daten aktualisiert werden. Von daher würde
ich davon ausgehen, dass das Verlassen eines synchronized Block das
synchronisieren aller Daten in allen Threads nach sich zieht. Da würde
quasi bedeuten, dass ein Thread, der garnichts mit dem synchronized
Block zu tun hat trotzdem kurzfristig angehalten werden müsste damit die
Daten synchronisiert werden. Jedenfalls machen die AtomicXY-Klassen dann
weitaus mehr Sinn für mich.

Gruss theo
Bernd Hohmann
2009-02-16 21:00:04 UTC
Permalink
Von daher würde ich davon ausgehen, dass das Verlassen eines
synchronized Block das synchronisieren aller Daten in allen Threads
nach sich zieht.
Hm, hm, hm... Ich habe mich bislang immer nur darauf verlassen, dass die
Daten der im Synchronized angefassten Variablen danach für alle Threads
gleich sind (und nicht alle Daten). Kann aber auch sein, dass wir das
gleiche meinen.
Da würde quasi bedeuten, dass ein Thread, der garnichts mit dem
synchronized Block zu tun hat trotzdem kurzfristig angehalten werden
müsste damit die Daten synchronisiert werden. Jedenfalls machen die
AtomicXY-Klassen dann weitaus mehr Sinn für mich.
Ich krabbel gerade von 1.1 nach 1.5. Bis ich AtomicXY flächendeckend
einsetzen kann müssen alle Kunden nach 1.5 und höher - und das wird mich
wohl noch bis zu meiner Rente beschäftigen ;-)

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Raffael Herzog
2009-02-16 21:37:23 UTC
Permalink
Post by Jochen Theodorou
Jedenfalls machen die AtomicXY-Klassen dann
weitaus mehr Sinn für mich.
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben dafür
da ist, lockless Threadsafety zu implementieren. Die Idee ist, dass man
einen Status ausliest, anhand dieses Status einen neuen Status berechnet,
und danach in einer CPU-Instruktion sagt: "Wenn der Status jetzt immer noch
so ist, wie zuvor, dann setze den Status jetzt neu (compareAndSet(expected,
newValue)). Ist der Status nicht mehr gleich, berechnet man halt nochmal
neu. In manchen Fällen ist das effizienter, als alle Threads zu serialisie-
ren, sämtliche Lock-Implementationen arbeiten beispielsweise so.

Gruss,
Raffi
--
Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is *nothing* like Shakespeare!

***@raffael.ch · PGP Key 5D1FF5F4 · http://www.raffael.ch/
Jochen Theodorou
2009-02-16 22:00:17 UTC
Permalink
Post by Raffael Herzog
Post by Jochen Theodorou
Jedenfalls machen die AtomicXY-Klassen dann
weitaus mehr Sinn für mich.
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben dafür
da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
Post by Raffael Herzog
Die Idee ist, dass man
einen Status ausliest, anhand dieses Status einen neuen Status berechnet,
und danach in einer CPU-Instruktion sagt: "Wenn der Status jetzt immer noch
so ist, wie zuvor, dann setze den Status jetzt neu (compareAndSet(expected,
newValue)). Ist der Status nicht mehr gleich, berechnet man halt nochmal
neu. In manchen Fällen ist das effizienter, als alle Threads zu serialisie-
ren, sämtliche Lock-Implementationen arbeiten beispielsweise so.
Ein Beispiel wäre vielleicht ein Zähler von irgendwas... sagen wir mal
er soll zählen wie oft eine Methode aufgerufen wurde. Da das
Inkrementieren selbst nicht atomar ist kann man mit (z.B.) AtomicInteger
sicherstellen, dass korrekt inkrementiert wird.... und das ohne locking,
sofern AtomicInteger ohne locks arbeitet (was die Spec nicht garantiert)

Gruss theo
Bernd Eckenfels
2009-02-16 22:03:14 UTC
Permalink
Post by Jochen Theodorou
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben daf?r
da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
Es wird compareAndSet benutzt, wie hier beschrieben:

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/package-summary.html

Gruss
Bernd
Jochen Theodorou
2009-02-16 23:49:38 UTC
Permalink
Post by Bernd Eckenfels
Post by Jochen Theodorou
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben daf?r
da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/package-summary.html
ja, aber das sit doch eine Methode, keine CPU-Instruktion

Gruss theo
Patrick Roemer
2009-02-16 23:57:03 UTC
Permalink
Post by Jochen Theodorou
Post by Bernd Eckenfels
Post by Jochen Theodorou
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben daf?r
da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/package-summary.html
ja, aber das sit doch eine Methode, keine CPU-Instruktion
| The specifications of these methods enable implementations to employ
| efficient machine-level atomic instructions that are available on
| contemporary processors.

Genauer geht's ja schlecht auf der Abstraktionsebene.

Viele Gruesse,
Patrick
Bernd Hohmann
2009-02-16 22:06:58 UTC
Permalink
Da das Inkrementieren selbst nicht atomar ist [...]
dh. ich muss bei "int i=0; i++;" damit rechnen, dass auch mal 0.5
drinstehen kann weil die Sunnies auch das gründlich vermasstelt haben?

Bernd
--
Visit http://www.nixwill.de and http://www.spammichvoll.de
Bernd Eckenfels
2009-02-16 22:20:37 UTC
Permalink
Post by Bernd Hohmann
dh. ich muss bei "int i=0; i++;" damit rechnen, dass auch mal 0.5
drinstehen kann weil die Sunnies auch das gr?ndlich vermasstelt haben?
Wenn du mit mehr als einem Thread i++ machst kann es sein dass ein increment
verlorengeht.

Gruss
Bernd
Patrick Roemer
2009-02-16 23:24:39 UTC
Permalink
Post by Bernd Hohmann
Da das Inkrementieren selbst nicht atomar ist [...]
dh. ich muss bei "int i=0; i++;" damit rechnen, dass auch mal 0.5
drinstehen kann weil die Sunnies auch das gründlich vermasstelt haben?
Wenn zwei Threads gleichzeitig das i++ ausfuehren und Du hinterher durch
die Anzahl der Threads teilst, ja. Dann sind es aber nicht unbedingt die
Sunnies, die das vermasselt haben. :)

Viele Gruesse,
Patrick
Jochen Theodorou
2009-02-16 23:50:51 UTC
Permalink
Post by Bernd Hohmann
Da das Inkrementieren selbst nicht atomar ist [...]
dh. ich muss bei "int i=0; i++;" damit rechnen, dass auch mal 0.5
drinstehen kann weil die Sunnies auch das gründlich vermasstelt haben?
mit lokaler Variable brauchst du dir da keine Gedanken machen, aber wenn
es ein Feld ist und mehrere Threads das ++ ausführen, dann kann es schon
sein, das ein ++ mal verloren geht.

Gruss theo
Raffael Herzog
2009-02-16 22:17:10 UTC
Permalink
Post by Jochen Theodorou
Post by Raffael Herzog
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben
dafür da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
cmpxchg auf Intel-CPUs, ja. In der Implementation wird zwar eine native
Methode aufgerufen, aber HotSpot sieht dann schon, was das ist. Natürlich
braucht man auch eine CPU, die das kann, aber das können alle modernen
CPUs.

http://en.wikipedia.org/wiki/Compare-and-swap

Wobei Java eine Variante von Compare-and-Swap verwendet, eben, wie der Name
der Methode schon sagt, Compare-and-Set. Compare-and-Swap würde das erneute
get() bei jedem Schleifendurchlauf sparen. Aber auch hier wird HotSpot
bemerken, was Sache ist ... ;)
Post by Jochen Theodorou
Ein Beispiel wäre vielleicht ein Zähler von irgendwas... sagen wir mal
er soll zählen wie oft eine Methode aufgerufen wurde. Da das
Inkrementieren selbst nicht atomar ist kann man mit (z.B.) AtomicInteger
sicherstellen, dass korrekt inkrementiert wird.... und das ohne locking,
sofern AtomicInteger ohne locks arbeitet (was die Spec nicht garantiert)
Richtig, genau so ist z.B. incrementAndGet() implementiert:

public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}

Gruss,
Raffi
--
Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is *nothing* like Shakespeare!

***@raffael.ch · PGP Key 5D1FF5F4 · http://www.raffael.ch/
Jochen Theodorou
2009-02-16 23:54:26 UTC
Permalink
Post by Raffael Herzog
Post by Jochen Theodorou
Post by Raffael Herzog
Die Atomic-Klassen benutzen eine spezielle CPU-Instruktion, die eben
dafür da ist, lockless Threadsafety zu implementieren.
lockless sicher, aber spezielle CPU-Instruktion? Steht das irgendwo?
cmpxchg auf Intel-CPUs, ja. In der Implementation wird zwar eine native
Methode aufgerufen, aber HotSpot sieht dann schon, was das ist. Natürlich
braucht man auch eine CPU, die das kann, aber das können alle modernen
CPUs.
können Intel kompatible seit dem 486er scheints... Den Befehl hatte ich
wohl nie benutzt

[...]
Post by Raffael Herzog
Post by Jochen Theodorou
Ein Beispiel wäre vielleicht ein Zähler von irgendwas... sagen wir mal
er soll zählen wie oft eine Methode aufgerufen wurde. Da das
Inkrementieren selbst nicht atomar ist kann man mit (z.B.) AtomicInteger
sicherstellen, dass korrekt inkrementiert wird.... und das ohne locking,
sofern AtomicInteger ohne locks arbeitet (was die Spec nicht garantiert)
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
las ich dass das erste mal sah, befand ich das für höchst seltsamen
Code. Aber inzwischen verstehe ich das besser und es macht dann doch Sinn ;)

Gruss theo
Paul Ebermann
2009-02-20 02:06:42 UTC
Permalink
Post by Jochen Theodorou
Von daher würde
ich davon ausgehen, dass das Verlassen eines synchronized Block das
synchronisieren aller Daten in allen Threads nach sich zieht. Da würde
quasi bedeuten, dass ein Thread, der garnichts mit dem synchronized
Block zu tun hat trotzdem kurzfristig angehalten werden müsste damit die
Daten synchronisiert werden.
Nein. Wenn ein synchronized-Block verlassen wird, werden alle Daten, die
dieser Thread bisher schreibend angefasst hat, zum "Main memory" geschrieben
(und kommen da an, bevor der Thread ausserhalb des synchronized-Blocks
weitermachen oder ein anderer Thread den synchronized-Block (bzw. einen
beliebigen synchronized-Block mit selben Monitor) betreten darf).

Damit sind die Daten aber noch lange nicht bei den anderen Threads
angekommen - das sind sie erst dann garantiert, wenn der andere Thread
seinerseits in einen synchronized-Block reingelaufen ist. (Auch wenn das
ein anderer Monitor ist ... wobei dann aber wieder die Reihenfolge nicht
gesichert ist.)


Paul
--
Nun ludigxas: : ()
Jochen Theodorou
2009-02-20 10:29:10 UTC
Permalink
Post by Paul Ebermann
Post by Jochen Theodorou
Von daher würde
ich davon ausgehen, dass das Verlassen eines synchronized Block das
synchronisieren aller Daten in allen Threads nach sich zieht. Da würde
quasi bedeuten, dass ein Thread, der garnichts mit dem synchronized
Block zu tun hat trotzdem kurzfristig angehalten werden müsste damit die
Daten synchronisiert werden.
Nein. Wenn ein synchronized-Block verlassen wird, werden alle Daten, die
dieser Thread bisher schreibend angefasst hat, zum "Main memory" geschrieben
(und kommen da an, bevor der Thread ausserhalb des synchronized-Blocks
weitermachen oder ein anderer Thread den synchronized-Block (bzw. einen
beliebigen synchronized-Block mit selben Monitor) betreten darf).
Damit sind die Daten aber noch lange nicht bei den anderen Threads
angekommen - das sind sie erst dann garantiert, wenn der andere Thread
seinerseits in einen synchronized-Block reingelaufen ist. (Auch wenn das
ein anderer Monitor ist ... wobei dann aber wieder die Reihenfolge nicht
gesichert ist.)
gut... kann ich dem entnehmen, dass während des synchronized-Block, dann
also keine Daten an andere Threads gehen?

Gruss theo
Patrick Roemer
2009-02-20 12:34:19 UTC
Permalink
Post by Jochen Theodorou
Post by Paul Ebermann
Nein. Wenn ein synchronized-Block verlassen wird, werden alle Daten, die
dieser Thread bisher schreibend angefasst hat, zum "Main memory" geschrieben
(und kommen da an, bevor der Thread ausserhalb des synchronized-Blocks
weitermachen oder ein anderer Thread den synchronized-Block (bzw. einen
beliebigen synchronized-Block mit selben Monitor) betreten darf).
Damit sind die Daten aber noch lange nicht bei den anderen Threads
angekommen - das sind sie erst dann garantiert, wenn der andere Thread
seinerseits in einen synchronized-Block reingelaufen ist. (Auch wenn das
ein anderer Monitor ist ... wobei dann aber wieder die Reihenfolge nicht
gesichert ist.)
gut... kann ich dem entnehmen, dass während des synchronized-Block, dann
also keine Daten an andere Threads gehen?
AFAICS kannst Du das nicht.

Provided that all the constraints in Sections 8.3, 8.6, and 8.7 are
obeyed, a load or store operation may be issued at any time by any
thread on any variable, at the whim of the implementation.
[VMSpec 8.3]

...und auf Anhieb sehe ich in den aufgelisteten Absaetzen nichts, was
einem Thread, der gerade ein Lock haelt, verbieten wuerde, ein store
auszufuehren - er muss nur eine tatsaechliche Aenderung vorzuweisen haben.

Viele Gruesse,
Patrick
Bernd Eckenfels
2009-02-16 22:18:21 UTC
Permalink
[...] ist es da wirklich sichergestellt, dass alle Threads f?r isInit
und LC_INSTANCE die korrektenWerte haben?
Ja schon, sonst macht das synchronize nur zur H?lfte Sinn.
synchronized funktioniert aber nur wenn beide seiten (also sowohl lesend als
auch schreibend) synchronized sind. Wenn du den Zustand der Hilfsvariable
und die Instanzreferenz ohne einen synchronized block abpr?fst hast du keine
Garantie dass die beiden in der richtigen reihenfolge geschrieben wurden.

Gruss
Bernd
Jochen Theodorou
2009-02-17 00:43:40 UTC
Permalink
Post by Bernd Eckenfels
[...] ist es da wirklich sichergestellt, dass alle Threads f?r isInit
und LC_INSTANCE die korrektenWerte haben?
Ja schon, sonst macht das synchronize nur zur H?lfte Sinn.
synchronized funktioniert aber nur wenn beide seiten (also sowohl lesend als
auch schreibend) synchronized sind. Wenn du den Zustand der Hilfsvariable
und die Instanzreferenz ohne einen synchronized block abpr?fst hast du keine
Garantie dass die beiden in der richtigen reihenfolge geschrieben wurden.
oft macht das aber gar keinen so großen Unterschied.Wenn ich zum
Beispiel in einem Thread polle ob ein anderer noch etwas abzuarbeiten
hat, dann macht es meist nix aus, wenn just in diesem Moment der andere
Thread fertig wurde, seinen Status gerade aktualisiert, der pollenden
Thread aber noch den alten Zustand sieht.

Es kommt sehr stark auf den Einzelfall an...

Wenn ich mich richtig erinnere, dann wird zum Beispiel in
ConcurrentReaderHashMap von Doug Lea für Java 1.2 ein Array für die
innere Tabelle gehalten. Ein get benutzt dort direkt das Feld in dem die
Tabelle gespeichert ist, ohne dabei volatile oder synchronized zu
benutzen. Statt dessen gibt es ein synchronisiertes read der Art

protected final Entry[] getTableForReading() {
synchronized(barrierLock) {
return table;
}
}

barrierLock ist dabei eine Instanz einer inneren Klasse ohne besonderen
Inhalt, es benutzt nicht die locks, die man von java5 her kennt. Was
wird hier also gemacht? es wird einfach table zurückgegeben... Soweit
ich weiss, und das ist keineswegs sicher, findet bei einem synchronized
2x eine Synchronisierung der Daten statt. Einmal beim Eintritt, und dann
wieder beim Austritt. Ob das nur die Daten betrifft, die direkt im
synchronized verwendet werden oder auch andere weiss ich nicht genau,
aber da man auch Methoden aufrufen kann halte ich es für wahrscheinlich
dass da mehr als das Minimale passiert.

Jedenfalls wird hier ein mehrfaches lesen als Technik benutzt um zu
sehen ob sich etwas geändert hat, statt den Lesezugriff selbst zu
Synchronisieren.

Gruss theo
Bernd Eckenfels
2009-02-17 01:24:27 UTC
Permalink
oft macht das aber gar keinen so gro?en Unterschied.Wenn ich zum
Beispiel in einem Thread polle ob ein anderer noch etwas abzuarbeiten
hat, dann macht es meist nix aus, wenn just in diesem Moment der andere
Thread fertig wurde, seinen Status gerade aktualisiert, der pollenden
Thread aber noch den alten Zustand sieht.
Ja, nur das Problem ist: es k?nnte sein dass der Zustand selbst nur
teilweise sichtbar/geschrieben ist aber die Referenz auf das Objekt schon
gesetzt. In der Praxis macht das wohl keine VM aber duerfen darf sie das.

Gruss
Bernd
Jochen Theodorou
2009-02-17 13:48:33 UTC
Permalink
Post by Bernd Eckenfels
oft macht das aber gar keinen so gro?en Unterschied.Wenn ich zum
Beispiel in einem Thread polle ob ein anderer noch etwas abzuarbeiten
hat, dann macht es meist nix aus, wenn just in diesem Moment der andere
Thread fertig wurde, seinen Status gerade aktualisiert, der pollenden
Thread aber noch den alten Zustand sieht.
Ja, nur das Problem ist: es k?nnte sein dass der Zustand selbst nur
teilweise sichtbar/geschrieben ist aber die Referenz auf das Objekt schon
gesetzt. In der Praxis macht das wohl keine VM aber duerfen darf sie das.
Also erstmal hatte ich durchaus schon den Fall das eine Referenz auf das
Objekt schon da war, das Objekt selbst aber noch nicht initialisiert
war. Auf einem dicken Solarisrechner kann sowas schon vorkommen.
Allerdings wenn das schreiben innerhalb eines synchronized-Block
erfolgt, darf die VM es dann auch?

Gruss theo
Patrick Roemer
2009-02-17 14:04:02 UTC
Permalink
Post by Jochen Theodorou
Post by Bernd Eckenfels
Ja, nur das Problem ist: es k?nnte sein dass der Zustand selbst nur
teilweise sichtbar/geschrieben ist aber die Referenz auf das Objekt schon
gesetzt. In der Praxis macht das wohl keine VM aber duerfen darf sie das.
In der "Double Checked Locking Is Broken" Declaration wird ein Fall aus
der freien Wildbahn mit Symantec JIT aufgefuehrt. Ist natuerlich schon
reichlich aelter.
Post by Jochen Theodorou
Also erstmal hatte ich durchaus schon den Fall das eine Referenz auf das
Objekt schon da war, das Objekt selbst aber noch nicht initialisiert
war. Auf einem dicken Solarisrechner kann sowas schon vorkommen.
Allerdings wenn das schreiben innerhalb eines synchronized-Block
erfolgt, darf die VM es dann auch?
Warum nicht? AFAICS beziehen sich die Garantien (stark vereinfacht) nur
auf Sichtbarkeit im selben Thread und auf Abfolgen von T1:unlock -> T2:lock.

Viele Gruesse,
Patrick
Jochen Theodorou
2009-02-17 14:55:06 UTC
Permalink
Post by Patrick Roemer
Post by Jochen Theodorou
Post by Bernd Eckenfels
Ja, nur das Problem ist: es k?nnte sein dass der Zustand selbst nur
teilweise sichtbar/geschrieben ist aber die Referenz auf das Objekt schon
gesetzt. In der Praxis macht das wohl keine VM aber duerfen darf sie das.
In der "Double Checked Locking Is Broken" Declaration wird ein Fall aus
der freien Wildbahn mit Symantec JIT aufgefuehrt. Ist natuerlich schon
reichlich aelter.
Post by Jochen Theodorou
Also erstmal hatte ich durchaus schon den Fall das eine Referenz auf das
Objekt schon da war, das Objekt selbst aber noch nicht initialisiert
war. Auf einem dicken Solarisrechner kann sowas schon vorkommen.
Allerdings wenn das schreiben innerhalb eines synchronized-Block
erfolgt, darf die VM es dann auch?
Warum nicht? AFAICS beziehen sich die Garantien (stark vereinfacht) nur
auf Sichtbarkeit im selben Thread und auf Abfolgen von T1:unlock -> T2:lock.
also sagen wir es mal so... im gleichen Thread ist es nicht so wichtig.
Wichtig ist wann die Änderung der Referenz in einem anderem Thread
sichtbar werden kann. Wird ein foo=new Bar() erst sichtbar wenn der
Block verlassen wird, dann kann man sicher sein, dass keine
uninitialisierten Objekte bestehen werden.

Gruss theo
Patrick Roemer
2009-02-17 17:26:53 UTC
Permalink
Post by Jochen Theodorou
Post by Patrick Roemer
Post by Jochen Theodorou
Also erstmal hatte ich durchaus schon den Fall das eine Referenz auf das
Objekt schon da war, das Objekt selbst aber noch nicht initialisiert
war. Auf einem dicken Solarisrechner kann sowas schon vorkommen.
Allerdings wenn das schreiben innerhalb eines synchronized-Block
erfolgt, darf die VM es dann auch?
Warum nicht? AFAICS beziehen sich die Garantien (stark vereinfacht) nur
auf Sichtbarkeit im selben Thread und auf Abfolgen von T1:unlock -> T2:lock.
also sagen wir es mal so... im gleichen Thread ist es nicht so wichtig.
Naja, mir waere es schon wichtig, dass ich auch im gleichen Thread keine
halb instantiierten Objekte zu sehen bekomme... ;)
Post by Jochen Theodorou
Wichtig ist wann die Änderung der Referenz in einem anderem Thread
sichtbar werden kann. Wird ein foo=new Bar() erst sichtbar wenn der
Block verlassen wird, dann kann man sicher sein, dass keine
uninitialisierten Objekte bestehen werden.
...aber mir ging es nicht um wichtig oder nicht, sondern um die Faelle,
fuer die die Spec Garantien gibt bzw. ueberhaupt Aussagen trifft. Und
ich sehe bisher nichts, aus dem man irgendwelche Aussagen ueber Code in
einem synchronized-Block ohne weitere Nebenbedingungen (wie etwa
Interaktion mit einem synchronized-Block in einem anderen Thread)
ableiten koennte - eben abgesehen vom Verhalten innerhalb desselben
Threads, und dann ist das synchronized eh irrelevant.

Viele Gruesse,
Patrick
Patrick Roemer
2009-02-16 22:34:30 UTC
Permalink
Post by Jochen Theodorou
innerhalb besteht die Gefahr dass jemand in einem anderen Thread was
falsches liest. Und nach dem verlassen des synchronized block... ist es
da wirklich sichergestellt, dass alle Threads für isInit und LC_INSTANCE
die korrektenWerte haben?
Garantien bekommst Du nur, wenn Zugriffe mit potentiellen Konflikten in
einer "happens-before"-Beziehung stehen - und die gibt's zunaechst mal
nur mit (beiderseitiger) Synchronisierung oder volatile.

Viele Gruesse,
Patrick
Bernd Eckenfels
2009-02-16 22:14:54 UTC
Permalink
Bei der L?sung mit der Hilfsvariable sieht es meines Erachtens jedoch anders
aus, trotzdessen sich ?hnlich vom Ablauf her ansieht. Hier wird der
Wahrheitswert doch erst nach Erstellung UND der Referenzierung gesetzt oder
t?usche ich mich damit?
Es gibt bei Java keine Garantie dass ein Thread die Schreibzugriffe so
sieht, wie ein anderer Thread diese ausf?hrt. Das ist eine Festlegung des
Java Memory Models. Man muss mit volatile oder synchronized (oder new locks
oder Atomic*) arbeiten.

Gruss
Bernd
Divad
2009-02-17 01:34:02 UTC
Permalink
Post by Bernd Eckenfels
Es gibt bei Java keine Garantie dass ein Thread die Schreibzugriffe so
sieht, wie ein anderer Thread diese ausf?hrt. Das ist eine Festlegung des
Java Memory Models. Man muss mit volatile oder synchronized (oder new locks
oder Atomic*) arbeiten.
Gruss
Bernd
In der Ausgangsfrage war der entscheidende Teil ja snychronized:

private static boolean isInit = false; 
private synchronized static LazyCreationSingleton initialize() { 
 if (LC_INSTANCE == null) { 
 LC_INSTANCE = new LazyCreationSingleton(); 
 isInit = true; 
 } 
 return LC_INSTANCE; 

public static LazyCreationSingleton getInstance() { 
return (isInit) ? LC_INSTANCE : initialize(); 


Der Ablauf gestaltet sich doch folgendermaßen:
1) Solange kein Thread getInstance() aufruft, wird es keine Instanziierung
geben.
2) Der erste Thread und eventuell auch einige folgende werden bei Aufruf von
getInstance() an initialize() verwiesen, die dann synchronisiert das die
einzige Instanz einrichtet. Wenn der erste Thread die initialize() verläßt,
existiert
a) die Singleton-Instanz und
b) isInit ist true
3) Alle Threads die ebenfalls initialize() aufrufen mussten und auf Warten
gesetzt wurden, erhalten nun von dieser Methode die Singleton-Instanz
(LC_INSTANCE ist nicht null und die Instanz ist vollständig eingerichtet)
4) Alle späteren Threads, die getInstance() aufrufen können direkt mit der
Singleton-Instanz bedient werden, ohne dass dieser Zugriff synchronisiert
werden muss.

Bis jetzt ist mir nicht ersichtlich, inwiefern das dieser Lazy Creation-Ansatz
mit double-locking Hilfsvariable zu Problemen führen könnte. Wenn doch jemand
einen Einwand erhebt, höre ich gespannt hin :-)

Gute Nacht
Bernd Eckenfels
2009-02-17 01:51:23 UTC
Permalink
public?static?LazyCreationSingleton?getInstance()?{?
return?(isInit)???LC_INSTANCE?:?initialize();?
}?
nein...

Gruss
Bernd
Jochen Theodorou
2009-02-16 15:40:03 UTC
Permalink
Divad schrieb:
[...]
Post by Divad
/* Nun die Änderungen */
private static boolean isInit = false;
private synchronized static LazyCreationSingleton initialize() {
if (LC_INSTANCE == null) {
LC_INSTANCE = new LazyCreationSingleton();
isInit = true;
}
return LC_INSTANCE;
}
public static LazyCreationSingleton getInstance() {
return (isInit) ? LC_INSTANCE : initialize();
}
[...]
Post by Divad
Ich habe es oberflächlich getestet und da führte es zu keinen Exceptions.
Solange isInit false liefert, rufen alle Threads die synchronisierte Methode
initialize() auf. Sobald true, kann die Einzel-Instanz direkt zurückgegeben
werden.
ja, schöne Diskussion... ich sag mal was ich denke und ich hoffe die
anderen korrigieren mich, wenn ich was falsches sage.

prinzipiell hast du bei code der Form
Post by Divad
LC_INSTANCE = new LazyCreationSingleton();
isInit = true;
3 Teile:

LC_INSTANCE = uninitialisierte Version von LazyCreationSingleton
Initialisierung des LC_INSTANCE-Wertes mit ()
isInit = true;

diese 3 Teile können in beliebiger Reihenfolge ausgeführt werden. Das
bedeutet dann aber auch, dass isInit true sein kann, bevor LC_INSTANCE
einen korrekten Wert hat.

Ein synchronized ändert daran meiner Meinung nach nichts.

Wenn du also in eine unsychnronisierten Methode den Wert von isInit
nachfragst, dann kann es sein, dass dies den Wert true hat, aber
LC_INSTANCE null ist, oder noch schlimmer, den uninitialisierten Wert hat.

Im Prinzip hast du damit also das gleiche Problem wie beim double
checked locking, nur dass du nicht LC_INSTANCE irekt prüfst und das null
oder "teilweise initialisiert" sein könnte, sondern, du prüfst isInit...
aber mit den gleichen Problemen in LC_INSTANCE

Gruss theo
Bernd Eckenfels
2009-02-16 22:12:46 UTC
Permalink
Ein synchronized ?ndert daran meiner Meinung nach nichts.
Doch, aber nur wenn es bei allen Lesezugriffen benutzt wird, da die
Synchronized garantie eben ist, dass andere Threads die Updates danach sehen
(und kein Reordering ?ber Synchronized Grenzen hinaus passieren darf).

Aber: ich denke diese Optimierungen sind vollkommen unn?tig, das
synchronisieren eines einzelnen Nullchecks macht doch gar keine Probleme in
den meisten F?llen.

Bei engen Schleifen in denen es auffallen k?nnte kann man das getInstance()
ja einfach davor machen.

Gruss
Bernd
Jochen Theodorou
2009-02-17 13:50:51 UTC
Permalink
Post by Bernd Eckenfels
Ein synchronized ?ndert daran meiner Meinung nach nichts.
Doch, aber nur wenn es bei allen Lesezugriffen benutzt wird, da die
Synchronized garantie eben ist, dass andere Threads die Updates danach sehen
(und kein Reordering ?ber Synchronized Grenzen hinaus passieren darf).
Aber: ich denke diese Optimierungen sind vollkommen unn?tig, das
synchronisieren eines einzelnen Nullchecks macht doch gar keine Probleme in
den meisten F?llen.
Bei engen Schleifen in denen es auffallen k?nnte kann man das getInstance()
ja einfach davor machen.
es macht im vorliegenden Fall vielleicht keinen großen Unterschied, aber
ich würde es gerne allgemein wissen ;)

Gruss theo
m***@heinerkuecker.de
2009-02-17 10:07:50 UTC
Permalink
Post by Divad
ich bin auf mehreren Seiten darauf gestoßen, dass bei der Erzeugung von Lazy
Creation-Singletons die Performance-Einbußen durch die dauernde Synchronisation
von Threads in Java nicht zu umgehen sei.
Falls vorhanden, wo liegt die Zwickmühle?
Dies ist sozusagen ein Klassiker,
da findet man jede Menge drüber.

Ein problematischer Fall ist, wenn man dem
Singleton noch eine Information zur Initialisierung
mitgeben muss, zum Beispiel einer Log-Klasse
den Pfad zum Message-Bundle und diesen Pfad
muss man aus einer Properties-Datei lesen.

Es gibt auch noch Unterschiede je nach Java-Version.

http://www.google.de/search?q=double+checked+lock+idiom+java


Angelika Langer hat dazu einiges in Java-Spektrum und Java-Magazin
geschrieben:

http://www.google.de/search?hl=de&q=angelika+langer+java+memory+model&btnG=Suche&meta=


Bei Heinz Kabutz gibt es auch eine Menge Info

http://www.google.de/search?hl=de&q=Heinz+Kabutz+double+cheched+locking&meta=


Michael Hüttermann hatte mal was dazu auf seiner Web-Site,
die ist leider nicht mehr da.

Grüsse
Heiner
Wanja Gayk
2009-02-20 20:51:27 UTC
Permalink
Divad said...
Post by Divad
Hallo zusammen,
ich bin auf mehreren Seiten darauf gestoßen, dass bei der Erzeugung von Lazy
Creation-Singletons die Performance-Einbußen durch die dauernde Synchronisation
von Threads in Java nicht zu umgehen sei.
Das ist falsch.
Die Lösung ist erstaunlich einfach:

class Singleton{
private static class LazySingletonHolder {
public static Singleton instance = new Singleton();
}

public static SingletongetInstance() {
return LazySingletonHolder.instance;
}
}

Erklärung:
Die SingletonHolder-Klasse wird erst geladen, wenn auf sie zugefriffen
wird, also beim Aufruf von "LazySingletonHolder.instance".
Der Classloader stellt immer sicher, dass eine Klasse initialisiert ist,
bevor auf sie zugegriffen wird, also ist diese Lösung sowohl
Threadsicher, als auch Lazy.

Genaueres unter:
http://www.ibm.com/developerworks/java/library/j-jtp03304/

Gruß,
-Wanja-
--
Klingon function calls do not have 'parameters', they have 'arguments' -
and they always win them.
[Nele Abels in dsg]
Lesen Sie weiter auf narkive:
Loading...