Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thread-Zugriff auf Klassenvariablen während statischer Initialisierung

1 view
Skip to first unread message

Achim Peters

unread,
Apr 4, 2002, 4:11:57 PM4/4/02
to
Hi folks,

Kann es sein, dass ein Thread, den man innerhalb der statischen
Initialisierung einer Klasse startet, nicht auf statische Felder dieser
selben Klasse zugreifen kann, solange die statische Initialisierung
nicht beendet ist?

Ich habe im aktuellen Projekt eine Klasse Config, die ein Property-File
in einem privaten Properties-Object "options" cached und entsprechende
statische Properties-Methoden zur Verfügung stellt.

a) Das File soll beim Laden der Klasse erstmalig eingelesen werden.

b) Weiterhin soll bei Änderungen von aussen am Property-File (z. B. mit
Editor) dieses erneut eingelesen werden. Dazu setzt die Klasse in der
statischen Initialisierung einen Thread auf, der zyklisch das
lastModified() des Files prüft und es ggf. mit options.load() neu lädt
(den Thread nenne ich mal "Einlese-Thread").

c) Weiterhin sollen andere Klassen die Möglichkeit haben, in eigenen
Threads auf solche Änderungen zu warten, um sich dann "ihre" Werte neu
zu holen. Dazu können die Threads der anderen Klassen (in einer
Config.Methode gekapselt) in ein options.wait() gehen und der
Einlese-Thread macht beim evtll. Einlesen dann ein options.notifyAll().

So weit, so gut.

Was liegt nun näher, als das erstmalige Einlesen des Files beim Laden
der Config-Klasse unter a) ebenfalls vom Einlese-Thread durchführen zu
lassen, anstatt das Einlesen des Files an zwei Stellen aufzurufen?

Man muss ja nur dafür sorgen, dass die statische Initialisierung der
Klasse am Ende nach dem EinleseThread.start() noch auf das notifyAll
wartet, um bei anderen Klassen, die die Config-Properties-Methoden
aufrufen, nicht in eine Race-Condition zu laufen.

Das hatte ich so implementiert und wartete allerdings endlos. Debugging
lieferte, dass der Einlese-Thread nach dem start() anläuft, aber nicht
auf die statische Klassenvariable options zugreifen kann, bzw. dabei
geblockt wird, und erst nach dem Verlassen der statischen
Initialisierung wieder freigegeben wird.

Kann man den Zugriff irgendwie ermöglichen?

Ich rufe inzwischen das Einlesen des Files auch einmal ausserhalb des
Threads auf und habe damit das Problem umgangen. Interessieren würde
mich eine Erläuterung zu diesem Phänomen aber trotzdem.

So kurz wie IMHO möglich die Klasse Config:

import java.util.*;

public class Config {
private static Properties options = new Properties();

static {
System.out.println("Config: Statische Initialisierung gestartet");

// Refresh Thread definieren
Thread refreshThread = new Thread(new Runnable() {
public void run() {
System.out.println("Thread angelaufen");

// Dummy-Zugriff auf options, um zu zeigen, dass der Thread hier hängt
if (options == null)
System.out.println("options ist null");
else
System.out.println("options ist nicht null");

while (true) {
// Hier würde zyklisch lastModified geprüft
synchronized (options) {
// options.load();
System.out.println("notify");
options.notifyAll();
}
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {};
}
}
});

/* Auf Daemon setzen (am Ende nicht drauf warten) */
refreshThread.setDaemon(true);

/* Auffi geht's */
refreshThread.start();

System.out.println("Thread gestartet");

try {
// Während des sleep sollte der Thread schon laufen
// er tut es aber nicht, daher kann man mit
// options.wait() auf ewig warten.
Thread.sleep(6000);
}
catch (InterruptedException e) {};

System.out.println("Config init() end");
}

public static String getProperty (String key) {
// Werte abholen
return null;
}

}

Und die Testklasse Configtst

public class Configtst
{
public static void main(String[] args) {
System.out.println("--- Configtst ---");

// Laden und initialisieren der Klasse Config anstossen
Config.getProperty("dummy");
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {};

System.out.println("--- Configtst end ---");
}
}

liefert zusammen den Output:
--- Configtst ---
Config: Statische Initialisierung gestartet
Thread gestartet
Thread angelaufen
<-----> Hier ist eine Pause von ca. 6 s.
Config init() end
options ist nicht null
notify
notify
notify
notify
notify
notify
notify
notify
notify
notify
--- Configtst end ---

Tnx!

Bye
Achim

bernd hohmann

unread,
Apr 4, 2002, 7:49:02 PM4/4/02
to
On Thu, 04 Apr 2002 23:11:57 +0200, Achim Peters wrote:

achim,

ohne dein problem um diese uhrzeit richtig zu durchschauen ist mir
nur eines aufgefallen:

> Ich habe im aktuellen Projekt eine Klasse Config, die ein Property-File
> in einem privaten Properties-Object "options" cached und entsprechende
> statische Properties-Methoden zur Verfügung stellt.
>
> a) Das File soll beim Laden der Klasse erstmalig eingelesen werden.

das würde ich sein lassen.

statische initialisierungen im deklarationsteil haben den nachteil,
dass sie im fehlerfall eine exception der sorte "keine ahnung was da
nicht gelaufen ist, aber es ist nicht gelaufen" hervorbringen.

sowas hat mir mal 2 tage sucharbeit verschafft weil irgendein tool im
deklarationsteil eine datei mit texten gesucht hat, die für die
gerade genutzte sprache halt eben nunmal nicht da war.

bernd

---
// Windows bringt ihnen mehr Spass bei der Arbeit !
class WinFun{public static void main(String[]a){String s="\u0009";for(int i=0;
i<16;i++)s=s+"\u0008";while(true) System.out.print(s);}} // hdc

Andreas Senft

unread,
Apr 5, 2002, 12:36:24 AM4/5/02
to
bernd hohmann wrote:
>
> On Thu, 04 Apr 2002 23:11:57 +0200, Achim Peters wrote:
>
> achim,
>
> ohne dein problem um diese uhrzeit richtig zu durchschauen ist mir
> nur eines aufgefallen:
>
> > Ich habe im aktuellen Projekt eine Klasse Config, die ein Property-File
> > in einem privaten Properties-Object "options" cached und entsprechende
> > statische Properties-Methoden zur Verfügung stellt.
> >
> > a) Das File soll beim Laden der Klasse erstmalig eingelesen werden.
>
> das würde ich sein lassen.
>

ACK. Ich würde das auch lieber anders lösen (z.B. init-Methode, oder
Initialisieren beim ersten Zugriff).

Im Ursprungscode kann ich so direkt das Problem nicht identifizieren.
Ich vermute, daß die Synchronisierung des ClassLoaders da eventuell
querschießt. Nach Möglichkeit würde ich auf das Starten von Threads im
static-Initializer verzichten. Zur Not bau doch ein Singleton, dann
kannst Du das alles in den Konstruktor verfrachten.

Gruß,
Andreas

Achim Peters

unread,
Apr 5, 2002, 5:05:58 PM4/5/02
to
Hi Andreas,

> > > a) Das File soll beim Laden der Klasse erstmalig eingelesen werden.
> >
> > das würde ich sein lassen.
> >
>
> ACK. Ich würde das auch lieber anders lösen (z.B. init-Methode, oder
> Initialisieren beim ersten Zugriff).

Ich fand es aber so eleganter. Das ganze läuft unter Websphere mit
vielen Servlets und von jedem jeweils mehrere. Da ich nicht weiß,
welches das erste Servlet ist, das initialisiert wird, müsste in jedem
das init() aufgerufen werden und im init() müsste ich mich mit einem "if
(!init)" gegen nachfolgend und mit "synchronized" gegen parallel
initialisierte Servlets schützen.

Bei Initialisieren beim ersten Zugriff muesste ich in jeder
public-Methode der Klasse ein "if (!init) init()" einbauen und das ganze
wieder synchronized.

Da scheint mir das Unterbringen in der statischen Initialisierung
wesentlich eleganter, weil das sämtliche obige Logik schon automatisch
mit drin hat, ohne dass ich die extra kodieren muss.



> Im Ursprungscode kann ich so direkt das Problem nicht identifizieren.
> Ich vermute, daß die Synchronisierung des ClassLoaders da eventuell
> querschießt.

Sowas in der Richtung.

> Nach Möglichkeit würde ich auf das Starten von Threads im
> static-Initializer verzichten.

Warum? Klappt ansonsten - auch in anderen Klassen des Projekts für
andere Zwecke - wunderbar.

> Zur Not bau doch ein Singleton, dann
> kannst Du das alles in den Konstruktor verfrachten.

Was passierte denn dadrin _nicht_ während der statischen
Initialisierung? Der Konstruktor würde dann doch eben während jener
statischen Initialisierung über ein "private final static CONFIG = new
Config()" aufgerufen werden müssen - wo sollte da ein Unterschied sein?

Bye
Achim

bernd hohmann

unread,
Apr 5, 2002, 6:03:58 PM4/5/02
to
On Sat, 06 Apr 2002 00:05:58 +0200, Achim Peters wrote:

> Ich fand es aber so eleganter. Das ganze läuft unter Websphere mit
> vielen Servlets und von jedem jeweils mehrere. Da ich nicht weiß,
> welches das erste Servlet ist, das initialisiert wird, müsste in jedem
> das init() aufgerufen werden und im init() müsste ich mich mit einem "if
> (!init)" gegen nachfolgend und mit "synchronized" gegen parallel
> initialisierte Servlets schützen.

für jedes servlet wird einmalig nach dem laden die .init() bzw.
.init(ServletConfig) aufgerufen.

baust dir mit "private static Object objLock = new Object();" im
deklarationsteil ein lockobjekt zusammen.

dazu noch ein "private static boolean blnHasInit=false"

public init(ServletConfig s)
super.init(s);
synchronized (objLock) {
if (blnHasInit)
return;
myThread=new Thread(...);
myThread.start();
while(!blnHasInit)
sleep(100);
}
public void run() {
blnHasInit=true;
while(true) {
// machwas
}
}

und schon ist schluss.

wichtig ist, dass das "blnHasInit" erst IM thread auf true gesetzt
wird und dein init solange wartet, bis der thread angelaufen ist (das
dauert nämlich seine zeit bis der thread gestartet ist).

wenn du das auf mehrere rechner verteilst, muss man das noch anders
machen.

EIN problem bleibt: die servletinstanzen werden bei vielen runnern
erst dann gestartet, wenn sie gebraucht werden. die dabei
aufkommenden race conditions sind legendär weil man sie erst unter
wechsellast bemerkt. die obige methode hilft da aber weil das .init()
zuerst zuschlägt.


wenn du bei der auswahl des servers und des runners freie hand hast,
würde ich dir den xitami-webserver und unseren servletrunner ans herz
legen (http://runner.conacom.de).

hier werden alle servletsessions beim starten sofort initialisiert
(hintereinander), permanent an den webserver gebunden und alle
threads gestartet.

damit hast du den vorteil einer definierten last, was bei grossen
datenbanken im backend wesentlich einfacher zu handeln ist, als zb.
websphere.

> Bei Initialisieren beim ersten Zugriff muesste ich in jeder
> public-Methode der Klasse ein "if (!init) init()" einbauen und das ganze
> wieder synchronized.

musst du nicht. .init(..) wird durchlaufen, ehe was anderes mit dem
servlet passiert. wenn du das erste .init(..) gesehen hast, musst du
nur noch nachfolgende teil-initialisierungen wie oben gezeigt
sperren.

bernd

--
xitami servletrunner http://runner.conacom.de

Achim Peters

unread,
Apr 6, 2002, 6:13:12 PM4/6/02
to
Hi bernd,

> > im init() müsste ich mich mit einem "if
> > (!init)" gegen nachfolgend und mit "synchronized" gegen parallel
> > initialisierte Servlets schützen.
>

> synchronized (objLock) {
> if (blnHasInit)

> und schon ist schluss.

:-)) "Schon ist Schluss"? Stehe ich jetzt auf dem Schlauch, oder habe
ich mich (oben von Dir gequotet) missverständlich ausgedrückt? Dass man
_genau das_ machen muss, beschrieb ich doch als den Nachteil ...

Genau das "if" und das "sychronized" spare ich mir in der statischen
Initialisierung, wie ich schon schrieb. IMHO sauberer, wesentlich
eleganter und aufwandsneutral. ;-) Wenn Du anderer Meinung bist ... ich
bin für alle Argumente offen. ;-)

> > Bei Initialisieren beim ersten Zugriff muesste ich in jeder
> > public-Methode der Klasse ein "if (!init) init()" einbauen und das ganze
> > wieder synchronized.
>
> musst du nicht. .init(..) wird durchlaufen, ehe was anderes mit dem
> servlet passiert.

Wenn ich "Initialisieren beim ersten Zugriff" machen will, muss ich das
sehr wohl, und Andreas' zweiter Vorschlag lautete doch nun mal so:

| Initialisieren beim ersten Zugriff

Da kann ich ja auch nichts für. ;-)

Bye
Achim

Patrick Roemer

unread,
Apr 6, 2002, 7:31:22 PM4/6/02
to
Hallo,

Achim Peters wrote:

> Kann es sein, dass ein Thread, den man innerhalb der statischen
> Initialisierung einer Klasse startet, nicht auf statische Felder dieser
> selben Klasse zugreifen kann, solange die statische Initialisierung
> nicht beendet ist?

Nicht, dass ich es jetzt erklaeren koennte, aber bau mal
spasseshalber folgende Zeile ein:

> // Refresh Thread definieren
> Thread refreshThread = new Thread(new Runnable() {

private Properties options=Config.options;

Viele Gruesse,
Patrick

Karl Schmidt

unread,
Apr 6, 2002, 9:38:06 PM4/6/02
to
Achim Peters schrieb:

> Hi folks,
>
> Kann es sein, dass ein Thread, den man innerhalb der statischen
> Initialisierung einer Klasse startet, nicht auf statische Felder dieser
> selben Klasse zugreifen kann, solange die statische Initialisierung
> nicht beendet ist?

Natürlich nicht. Dein Thread ist eine eigene Klasse - wenn auch eine innerhalb
des Namespaces der äußeren. Wann immer Du auf eine Klasse zugreifen willst, muss
die JVM ungefähr dieses machen:

1. Evtl. Klasse laden
2. synchronisation auf Klasse, Status abfragen
3a. falls nicht initialisiert, initialisieren, weiter bei 4
3b. oder, falls schon ein anderer Thread initialisiert, wait(), weiter bei 5
3c. oder, falls alles klar, normal weitermachen bei 5
3d. oder, falls Klasse kaputt, Exception schmeißen
4a. Klasse ist fertig, wecke andere Threads, weiter bei 5
4b. Klasse ist nicht initalisierbar, wecke andere Threads und schmeiße Exception
5. falls Du lebend hier ankommst, mache mit dem Programm weiter.

Zu dem Zeitpunkt, zu dem Dein Thread startet, hat der erste Thread die Klasse
markiert als "wird gerade initialisiert, bitte warten...". Also wartet Dein
Thread brav, bis Config fertig initialisiert ist...

> Ich rufe inzwischen das Einlesen des Files auch einmal ausserhalb des
> Threads auf und habe damit das Problem umgangen.

Das ist m.E. auch die einzige Möglichkeit. Wenn Du das in der Initialisierung
machst, solltest Du auf jeden Fall auf Exceptions achten, sonst kriegst Du eine
ExceptionInInitializer oder so was feines.

Welche Java-Version hast Du? Solltest Du diese Konstruktion unter 1.1.x
ausprobieren, kannst Du Dein blaues Wunder erleben. Da sind die Spezifikationen
für das Entfernen von Klassen (gc) etwas ungenau. Da kann es schonmal passieren,
daß der gc die Klasse Config abschießt, weil keine lebenden Referenzen darauf
existieren. Wenn dann Dein kleiner Thread aus seinem Schläfchen erwacht, oder ein
anderes Objekt mal eben ein Property abfragt, wird die Klasse gerade nochmal
initialisiert, Du bekommst einen neuen zusätzlichen Thread und die Properties
könnten ein bißchen aus dem Tritt geraten. Insgesamt halte ich diese statische
Variante nicht für sehr glücklich. Mach lieber ein richtiges Objekt draus und
stelle es von der Hauptklasse Deiner Applikation aus global allen Interessenten
zur Verfügung.

--

MfG


Karl Schmidt
ICQ #15923569


bernd hohmann

unread,
Apr 7, 2002, 7:14:57 AM4/7/02
to
On Sun, 07 Apr 2002 01:13:12 +0200, Achim Peters wrote:

> > und schon ist schluss.
>
> :-)) "Schon ist Schluss"? Stehe ich jetzt auf dem Schlauch, oder habe
> ich mich (oben von Dir gequotet) missverständlich ausgedrückt? Dass man
> _genau das_ machen muss, beschrieb ich doch als den Nachteil ...

nein, du hattest geschrieben, dass du in jeder public methode den
check aufs init machen musst. und das kannst du dir ersparen.



> Genau das "if" und das "sychronized" spare ich mir in der statischen
> Initialisierung, wie ich schon schrieb. IMHO sauberer, wesentlich
> eleganter und aufwandsneutral. ;-) Wenn Du anderer Meinung bist ... ich
> bin für alle Argumente offen. ;-)

mach einfach den worst-case test:

class Test {
static {
int i = Integer.parseInt("1e");
}
public static void main(String args[]) {
new Test();
}
}

java.lang.ExceptionInInitializerError
at java.lang.Throwable.<init>(Throwable.java:63)
at java.lang.Error.<init>(Error.java:36)
at java.lang.LinkageError.<init>(LinkageError.java:28)
at
java.lang.ExceptionInInitializerError.<init>(ExceptionInInitializerErr
or.java:39)

eine schöne, nichtssagende fehlermeldung. gescheit debuggen ist auch
nicht möglich.

Achim Peters

unread,
Apr 7, 2002, 4:04:16 PM4/7/02
to
Hi Patrick,

> Nicht, dass ich es jetzt erklaeren koennte, aber bau mal
> spasseshalber folgende Zeile ein:
>
> > // Refresh Thread definieren
> > Thread refreshThread = new Thread(new Runnable() {
> private Properties options=Config.options;

Habe ich jetzt noch nicht. Was soll denn Deiner Meinung nach passieren,
wenn ich das tue? Ich prognostiziere, dass der Thread an der Stelle
genau so hängen bleiben wird, wie am "if (options == null)".
Prognostizierst Du etwas anderes?

Bye
Achim


Achim Peters

unread,
Apr 7, 2002, 4:25:55 PM4/7/02
to
Hi Bernd hohmann wrote:

> > :-)) "Schon ist Schluss"? Stehe ich jetzt auf dem Schlauch, oder habe
> > ich mich (oben von Dir gequotet) missverständlich ausgedrückt? Dass man
> > _genau das_ machen muss, beschrieb ich doch als den Nachteil ...
>
> nein, du hattest geschrieben, dass du in jeder public methode den
> check aufs init machen musst.

Andreas hatte zwei Vorschläge gemacht, auf die ich einging:

| (z.B. init-Methode, oder Initialisieren beim ersten Zugriff).

Zuerst nannte ich meine Gegenargumente zu seinem ersten Vorschlag
"Init-Methode". Das hattest Du auch gequotet:

| > > im init() müsste ich mich mit einem "if
| > > (!init)" gegen nachfolgend und mit "synchronized" gegen parallel
| > > initialisierte Servlets schützen.

Und genau das nanntest Du mir als Lösungsvorschlag, woraufhin ich Dich
auf diesen Umstand hinwies.

Und nun schreibst Du oben, dass ich das nicht geschrieben hätte? Du
hattest es ja selbst gequotet und ich extra noch mal mit ...

Weiterhin schreibst Du jetzt, ich habe stattdessen geschrieben, dass in
jeder public methode check aufs init gemacht werden müsse.

Das habe ich tatsächlich geschrieben, aber nicht "stattdessen", sondern
"außerdem". Dabei ging ich nämlich auf Andreas zweiten Vorschlag
"Initialisieren beim ersten Zugriff" ein.

> > Genau das "if" und das "sychronized" spare ich mir in der statischen
> > Initialisierung, wie ich schon schrieb. IMHO sauberer, wesentlich
> > eleganter und aufwandsneutral. ;-) Wenn Du anderer Meinung bist ... ich
> > bin für alle Argumente offen. ;-)
>
> mach einfach den worst-case test:
>
> class Test {
> static {
> int i = Integer.parseInt("1e");
> }
> }
>

> java.lang.ExceptionInInitializerError

Yep. Kenne ich. Deswegen ist alles im statischen Initialisierer auch in
einem try-catch-Block. Ich hatte den Code ja nur aufs wesentliche
gekürzt widergegeben. Aber selbst, wenn nicht: Dein Argument gegen Code
im statischen Initialisierer wäre also, dass man diesen Code
sinnvollerweise in einen try-catch-Block packt?

Ich stimme Dir zu, daß Code ohne try-catch innerhalb des statischen
Initialisierers wesentlich mistiger ist als ausserhalb. ;-)

Da die Anwendung aber sowieso alle Exceptions in eine ordentliche
Fehlermeldung für den Admin packen muss, bliebe mir das auch in Deiner
init-Methode nicht erspart. ;-)

Tnx für Deine Ausführungen!

Bye
Achim

Achim Peters

unread,
Apr 7, 2002, 4:11:13 PM4/7/02
to
Hi Karl,

> > Kann es sein, dass ein Thread, den man innerhalb der statischen
> > Initialisierung einer Klasse startet, nicht auf statische Felder dieser
> > selben Klasse zugreifen kann, solange die statische Initialisierung
> > nicht beendet ist?
>
> Natürlich nicht.

Dann bin ich beruhigt. ;-)

> Dein Thread ist eine eigene Klasse - wenn auch eine innerhalb
> des Namespaces der äußeren.

Stimmt! Dass der Thread nur im Namespace ist (und somit auf die Variable
auch ohne Klassenangabe zugreifen kann) hatte mich in die irre geführt.

> > Ich rufe inzwischen das Einlesen des Files auch einmal ausserhalb des
> > Threads auf und habe damit das Problem umgangen.
>
> Das ist m.E. auch die einzige Möglichkeit. Wenn Du das in der Initialisierung
> machst, solltest Du auf jeden Fall auf Exceptions achten,

Ja, alles (auch in den anderen Klassen, wo ich das so mache) ist in
try-catch gekapselt mit Fehlermeldung und allem. Das hatte ich nur
weggelassen, weil es für _mein_ Problem nicht relevant war.

> Welche Java-Version hast Du?

Entwicklung unter 1.2.x, Produktionsenvironment 1.3

> Solltest Du diese Konstruktion unter 1.1.x
> ausprobieren, kannst Du Dein blaues Wunder erleben. Da sind die Spezifikationen
> für das Entfernen von Klassen (gc) etwas ungenau. Da kann es schonmal passieren,
> daß der gc die Klasse Config abschießt, weil keine lebenden Referenzen darauf
> existieren.

:-))

> Insgesamt halte ich diese statische
> Variante nicht für sehr glücklich.

Warum?

> Mach lieber ein richtiges Objekt draus und
> stelle es von der Hauptklasse Deiner Applikation aus global allen Interessenten
> zur Verfügung.

Es gibt leider keine Hauptklasse, sondern viele parallele Servlets, und
ich weiß nicht, welches zuerst dran kommt.

Tnx schon mal für Deine Ausführungen!

Bye
Achim


bernd hohmann

unread,
Apr 7, 2002, 4:49:35 PM4/7/02
to
On Sun, 07 Apr 2002 22:25:55 +0200, Achim Peters wrote:

> Aber selbst, wenn nicht: Dein Argument gegen Code
> im statischen Initialisierer wäre also, dass man diesen Code
> sinnvollerweise in einen try-catch-Block packt?

es ist dein projekt und du musst mit deinem code leben.

es hindert dich auch keiner daran, die seiteneffekte dieser
programmierung solange herauszumendeln, bis es geht.

aber ehrlich gesagt bist du damit eher ein kandiat für den "darwin
award 2002"

Karl Schmidt

unread,
Apr 7, 2002, 4:59:01 PM4/7/02
to
Achim Peters schrieb:

> > Insgesamt halte ich diese statische
> > Variante nicht für sehr glücklich.
>
> Warum?

- Allgemeines Unwohlsein.
- Keine Vererbung, wenn man mal eine spezielle Version der Config braucht.
- schlechte Erfahrungungen mit Java 1.1 und rein statischen Klassen
- rumzickende IDEs
- Magenschmerzen
- Alpträume :-)

> > Mach lieber ein richtiges Objekt draus und
> > stelle es von der Hauptklasse Deiner Applikation aus global allen Interessenten
> > zur Verfügung.
>
> Es gibt leider keine Hauptklasse, sondern viele parallele Servlets, und
> ich weiß nicht, welches zuerst dran kommt.

Dann mach ein

public static final Config config = new Config();

in Deine Config Klasse. Da hätte ich nur halb soviele Bauchschmerzen.

Oder mache eine Superklasse für Deine Servlets, die das handelt...

Achim Peters

unread,
Apr 7, 2002, 6:29:52 PM4/7/02
to
Hi Karl,

> > > Insgesamt halte ich diese statische
> > > Variante nicht für sehr glücklich.

> - Keine Vererbung, wenn man mal eine spezielle Version der Config braucht.

Das schiene mir ein schlagkräftiges Argument - ich verstehe nur den
Hintergrund nicht und kann es nicht nachvollziehen. ;-)

Inwiefern "keine Vererbung"? Das einzige, was mir einfiele, wäre, dass
Du den Code, den ich im statischen Initialisierer ausführe, in einer
ggf. abgeleiteten Klasse unter Umständen nicht ausführen willst? Ich
nehme an, Dein weiter unten angeführtes Singelton soll da Abhilfe
schaffen? Im Singelton würde ich den Code aus dem statischen
Initialisierer in den Konstruktor verschieben. Aber damit würde ich in
der abgeleiteten Klasse doch diesen Code nur dann *nicht* ausführen,
wenn ich im Konstrunktor der abgeleiteten Klasse kein super.init()
mache.

Aber super.init() in Konstruktoren abgeleiteter Klassen ist doch quasi
obligatorisch, dachte ich?!

Was verstehe ich hier nicht richtig?

> - Magenschmerzen
> - Alpträume :-)

:-)) Hätte ich ja auch gerne, wenn ich nur wüsste, wieso ... ;-)

> > Es gibt leider keine Hauptklasse, sondern viele parallele Servlets, und
> > ich weiß nicht, welches zuerst dran kommt.
>

> Oder mache eine Superklasse für Deine Servlets, die das handelt...

Mein Argument gegen diese Alternative war, daß ich mich dann mit
synchronized gegen parallel und mit if (!init) gegen nachfolgend
initialisierte Servlets schützen muss, während ich im statischen
Initialisierer beides schon hausgemacht geliefert bekomme.

Eine Möglichkeit, schlüssig und nachvollziehbar Code einzusparen,
scheint mir grundsätzlich eleganter und wartbarer. Es sei denn, es gäbe
gewichtige Gründe dagegen - einen diskutieren wir gerade oben ... ;-)

Bye
Achim

Patrick Roemer

unread,
Apr 7, 2002, 7:05:57 PM4/7/02
to
Hallo,

Achim Peters wrote:

> > Nicht, dass ich es jetzt erklaeren koennte, aber bau mal
> > spasseshalber folgende Zeile ein:
> >
> > > // Refresh Thread definieren
> > > Thread refreshThread = new Thread(new Runnable() {
> > private Properties options=Config.options;
>
> Habe ich jetzt noch nicht. Was soll denn Deiner Meinung nach passieren,
> wenn ich das tue? Ich prognostiziere, dass der Thread an der Stelle
> genau so hängen bleiben wird, wie am "if (options == null)".

Tut er nicht. Auch options.wait() statt des sleep() tut es dann.
Probier es aus.

So ganz erklaeren kann ich mir das auch nicht. Ohne lokale
Variable wird erst in run() auf die interne access-Methode fuer
options zugegriffen, mit lokaler Variable schon und
ausschliesslich im Konstruktor des Threads. Aber warum der
Konstruktor dann noch randarf, wenn die Klasse fuer die
Initialisierung gesperrt ist... Ich hatte gehofft, einer der
VM-Gurus (Karl Schmidt?) koennte das aufklaeren.

Auch wenn es mit diesem 'Hack' funktioniert, wuerde ich das aber
nicht so machen wollen. Zum einen scheint es da VM-technisch zu
viele Unwaegbarkeiten zu geben (die von Karl erwaehnte
Problematik mit alten VMs duerfte wahrscheinlich auch im
Zusammenspiel mit Class-Reloading auftauchen, was ja bei
Servlet-Containern schon mal vorkommen soll), zum anderen finde
ich Bernds Einwand der Nicht-Debugbarkeit ziemlich schlagend.

Viele Gruesse,
Patrick

Karl Schmidt

unread,
Apr 7, 2002, 8:13:17 PM4/7/02
to
Patrick Roemer schrieb:

> > > > // Refresh Thread definieren
> > > > Thread refreshThread = new Thread(new Runnable() {
> > > private Properties options=Config.options;

> [snip]


> So ganz erklaeren kann ich mir das auch nicht. Ohne lokale
> Variable wird erst in run() auf die interne access-Methode fuer
> options zugegriffen, mit lokaler Variable schon und
> ausschliesslich im Konstruktor des Threads. Aber warum der
> Konstruktor dann noch randarf, wenn die Klasse fuer die
> Initialisierung gesperrt ist... Ich hatte gehofft, einer der
> VM-Gurus (Karl Schmidt?) koennte das aufklaeren.

Vielen Dank für die Blumen ;-)

Der Constructor wird im initialisierenden Thread selber ausgeführt. Erstens
einmal entsteht so nicht direkt ein Problem mit der Synchronisation, da es ja
derselbe Thread ist. Zweitens steht die JVM hier vor einem Dilemma: Die
Klasse ist noch nicht als fertig initialisiert markiert, aber die
Initialisierung findet im selben Thread statt, also kann der Thread nicht
warten. Zu diesem Zeitpunkt sind alle Felder schon initialisiert, zumindest
mit null. Also Augen zu und durch, der Constructor holt sich den Wert von
Config.options und macht weiter. Da das Feld options im Deklarationsteil
bereits belegt wird und alle Zuweisungen VOR der Ausführung irgendwelchen
Initialisierungscodes ausgeführt werden, funktioniert das also korrekt.

Folgender Code veranschaulicht den Punkt durch sein Versagen:

class TestChild extends TestParent {
static String childName = "Ich bin child";
static {
System.out.println("initialisiere child");
System.out.println("parent name=" + parentName);
System.out.println("child name=" + tc.childName);
}

public static void main(String args[]) {

System.out.println("Teste Testchild.main");
}
}

class TestParent {
static TestChild tc = new TestChild();
static String parentName = "Ich bin parent";
static {
System.out.println("initialisiere parent");
System.out.println("parent name=" + parentName);
System.out.println("child name=" + tc.childName);
}
}

Die Ausgabe ist:

initialisiere parent
parent name=Ich bin parent
child name=null <=====================
initialisiere child
parent name=Ich bin parent
child name=Ich bin child
Teste Testchild.main

Bevor irgendwas in der Subklasse passiert, wird erst die Superklasse fertig
initialisiert. Aber das Programm läuft trotz des eigentlich fehlerhaften
Zugriffs auf TestChild.childName ohne Fehler durch.

> Auch wenn es mit diesem 'Hack' funktioniert, wuerde ich das aber
> nicht so machen wollen. Zum einen scheint es da VM-technisch zu
> viele Unwaegbarkeiten zu geben (die von Karl erwaehnte
> Problematik mit alten VMs duerfte wahrscheinlich auch im
> Zusammenspiel mit Class-Reloading auftauchen, was ja bei
> Servlet-Containern schon mal vorkommen soll), zum anderen finde
> ich Bernds Einwand der Nicht-Debugbarkeit ziemlich schlagend.

Die alten VMs sind wohl selten ein praktisches Problem. Das Problem des
Debuggings ist da wohl das größere. Allerdings ist zumindest in diesem
Beispiel der Code in der Initialisierung so klein, daß sich ein Fehler wohl
finden liesse - wenn man weiß, wo man suchen muß. Und da die Reihenfolge der
Initialisierung klar definiert ist, würde ich das Beispiel nicht unbedingt
als unsicher ansehen.

Karl Schmidt

unread,
Apr 7, 2002, 8:27:22 PM4/7/02
to
Achim Peters schrieb:

> Hi Karl,
>
> > > > Insgesamt halte ich diese statische
> > > > Variante nicht für sehr glücklich.
> > - Keine Vererbung, wenn man mal eine spezielle Version der Config braucht.
>
> Das schiene mir ein schlagkräftiges Argument - ich verstehe nur den
> Hintergrund nicht und kann es nicht nachvollziehen. ;-)

Ich meine einfach dieses: Zugriffe auf statische Methoden werden nicht
dynamisch compiliert. Wenn Du später eine andere Implementation für Deine
Config Geschichte haben willst, mußt Du alles neu kompilieren. Wäre Config ein
Objekt, daß z.B. bei der Initialisierung übergeben wird (oder aus einer Factory
kommt), könntest Du eine neue Config Version durch eine Subklasse erstellen.
Jetzt wird die Config durch ein einfaches Property-File gefüllt. Vielleicht
willst Du später die Daten statt dessen aus einer Datenbank holen?

> Inwiefern "keine Vererbung"?

Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
aber nicht überschreiben. Alle Zugriffe erfolgen direkt.

> > - Magenschmerzen
> > - Alpträume :-)
>
> :-)) Hätte ich ja auch gerne, wenn ich nur wüsste, wieso ... ;-)

Einfach so. Riecht nach Problemen...

Patrick Roemer

unread,
Apr 7, 2002, 8:31:51 PM4/7/02
to
Hallo,

Karl Schmidt wrote:

> > So ganz erklaeren kann ich mir das auch nicht.

[...]


> Der Constructor wird im initialisierenden Thread selber ausgeführt.

*selbstpatsch* Latuernich. Irgendwie gebe ich, sobald es nach
VM-Interna riecht, meinen Verstand gerne mal vorher an der
Garderobe ab.

> Die alten VMs sind wohl selten ein praktisches Problem. Das Problem des
> Debuggings ist da wohl das größere. Allerdings ist zumindest in diesem
> Beispiel der Code in der Initialisierung so klein, daß sich ein Fehler wohl
> finden liesse - wenn man weiß, wo man suchen muß. Und da die Reihenfolge der
> Initialisierung klar definiert ist, würde ich das Beispiel nicht unbedingt
> als unsicher ansehen.

Naja, den Fehler kann man vielleicht noch schnell finden, aber
kaum vernuenftig abfangen.

Viele Gruesse,
Patrick

Achim Peters

unread,
Apr 8, 2002, 6:27:58 AM4/8/02
to
Hi Karl,

> Jetzt wird die Config durch ein einfaches Property-File gefüllt. Vielleicht
> willst Du später die Daten statt dessen aus einer Datenbank holen?

Klar, dazu ist das Design Mist. Allerdings würde da das hier an anderer
Stelle vorgeschlagene Verlagern in den Konstruktor wohl auch nicht viel
verbessern, da ja in einer ggf. abgeleiteten Klasse ein super()
obligatorisch wäre, oder? ;-)

Dann müsste das Ganze also in eine Art nicht-statische
initialLoad()-Methode, die nicht automatisch aufgerufen würde. Hmm,
macht die Benutzung der Klasse IMHO auch nicht einfacher ... ;-)

> > Inwiefern "keine Vererbung"?
>
> Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
> aber nicht überschreiben. Alle Zugriffe erfolgen direkt.

Verwechselst Du das mit private-Methoden? Bei mir klappt das
Überschreiben statischer Methoden einwandfrei:

public class Erbtest {
public static void staticMethod() {
System.out.println("Erbtest.staticMethod()");
}
}

public class Erbtest1 extends Erbtest {
public static void staticMethod() {
System.out.println("Erbtest1.staticMethod()");
}
}

public class Erbtesttst


{
public static void main(String[] args) {

System.out.println("--- Erbtest ---");
Erbtest.staticMethod();
Erbtest1.staticMethod();
System.out.println("--- Erbtest end ---");
}
}

liefert

--- Erbtest ---
Erbtest.staticMethod()
Erbtest1.staticMethod()
--- Erbtest end ---

Bye
Achim


Achim Peters

unread,
Apr 8, 2002, 6:18:35 AM4/8/02
to
Hi Patrick,

> > > > // Refresh Thread definieren
> > > > Thread refreshThread = new Thread(new Runnable() {
> > > private Properties options=Config.options;
> >

> > Ich prognostiziere, dass der Thread an der Stelle
> > genau so hängen bleiben wird, wie am "if (options == null)".
>
> Tut er nicht. Auch options.wait() statt des sleep() tut es dann.

Wow! Tnx!

> Probier es aus.

Ich glaub's Dir.

> Auch wenn es mit diesem 'Hack' funktioniert, wuerde ich das aber
> nicht so machen wollen.

:-)) Ich auch nicht.

> Problematik mit alten VMs duerfte wahrscheinlich auch im
> Zusammenspiel mit Class-Reloading auftauchen, was ja bei
> Servlet-Containern schon mal vorkommen soll

Yep, bei uns zumindest in der Test-Umgebeung laufend ... ;-/

> Bernds Einwand der Nicht-Debugbarkeit ziemlich schlagend.

Ich kann unter VAJ im statischen Initialisierer wunderbar break-pointen
und single-steppen - Ihr in Eurer Umgebung nicht?

Bye
Achim


Patrick Roemer

unread,
Apr 8, 2002, 7:13:29 AM4/8/02
to
Hallo,

Achim Peters wrote:

> > Bernds Einwand der Nicht-Debugbarkeit ziemlich schlagend.
>
> Ich kann unter VAJ im statischen Initialisierer wunderbar break-pointen
> und single-steppen - Ihr in Eurer Umgebung nicht?

Schon, aber wer will das? Eine aussagekraeftige Exception inkl.
Stacktrace ist mir weitaus lieber. Den Debugger schmeisse ich nur
alle Jubeljahre in spezialgelagerten Sonderfaellen an.

Abgesehen davon handelt es sich hier ja um eine Exception, die
durchaus im Produktivbetrieb vorkommen kann, und da gestaltet
sich das Debugging wahrscheinlich nicht so komfortabel.

Viele Gruesse,
Patrick

Karl Schmidt

unread,
Apr 8, 2002, 7:50:49 AM4/8/02
to
Achim Peters schrieb:

> Hi Karl,
>
> > Jetzt wird die Config durch ein einfaches Property-File gefüllt. Vielleicht
> > willst Du später die Daten statt dessen aus einer Datenbank holen?
>
> Klar, dazu ist das Design Mist. Allerdings würde da das hier an anderer
> Stelle vorgeschlagene Verlagern in den Konstruktor wohl auch nicht viel
> verbessern, da ja in einer ggf. abgeleiteten Klasse ein super()
> obligatorisch wäre, oder? ;-)

Ja, schon, aber da sind schon Möglichkeiten.

Schreibe Config als Interface oder abstracte Superklasse. Je nach Bedarf
implementierst Du die Datenquelle. Z.B. würde bei einer Version mit Datenbank der
Lesethread wegfallen, da ja nicht mehr auf eine Änderung des Property-Files gelauscht
werden müßte.

> Dann müsste das Ganze also in eine Art nicht-statische
> initialLoad()-Methode, die nicht automatisch aufgerufen würde. Hmm,
> macht die Benutzung der Klasse IMHO auch nicht einfacher ... ;-)

Da es reicht, diese init Methode einmal im statischen Initblock aufzurufen, wäre das
wohl kein wirkliches Problem. Die Implementation der init hängt dann von der
Subklasse ab.

> > > Inwiefern "keine Vererbung"?
> >
> > Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
> > aber nicht überschreiben. Alle Zugriffe erfolgen direkt.
>
> Verwechselst Du das mit private-Methoden? Bei mir klappt das
> Überschreiben statischer Methoden einwandfrei:

Das nennt man verdecken. Bei einer Vererbung würde auch dann Erbtest1.staticMethod()
aufgerufen, wenn der Referenztyp Erbtest ist. Praktisch heißt das: Wenn Du später
eine Klasse DatabaseConfig schreibst und beim statischen Ansatz bleibst, mußt Du an
jeder Stelle explizit den Aufruf der Config.getProperty() Methode auf
DatabaseConfig.getProperty() umschreiben. Bei Verwendung eines Objekts mit vererbten
Methoden reicht es, wenn Du an einer Stelle im Code (z.B. in Deiner Config Klasse) die
Subklasse statt der Superklasse erzeugst. Noch eleganter wäre eine Factory, die (z.B.
über System-Properties) selbst entscheidet, welche Version benutzt werden soll.

--

Mfg

Karl Schmidt
ICQ 15923569


Achim Peters

unread,
Apr 8, 2002, 6:20:47 PM4/8/02
to
Hi Karl,

> > > Jetzt wird die Config durch ein einfaches Property-File gefüllt. Vielleicht
> > > willst Du später die Daten statt dessen aus einer Datenbank holen?
> >
> > Klar, dazu ist das Design Mist.

> Schreibe Config als Interface oder abstracte Superklasse. Je nach Bedarf
> implementierst Du die Datenquelle.

Yep. Das wäre das saubere, weiter verwertbare Design. Allerdings war das
nicht gefordert und der Zeitdruck war eh schon extrem. ;-) Ich sollte ja
keine Config-Factory schreiben, sondern für unsere Anwendung ein
Config-File suchen und so einlesen und cachen, dass die Benutzung der
Klasse durch die Kollegen so einfach und so wenig
(kommunikations-)aufwendig wie möglich war ... Ein (automatisches)
Initialisieren beim ersten Zugriff, durch wen auch immer, schien mir
dieser Forderung am ehesten gerecht zu werden ... ;-)

Wenn wir im Release x.y mal noch eine zweite Art Config brauchen, kann
ich das Interface oder die abstrakte Superklasse ja immer noch drüber
hängen, sagt der PL. ;-)

Andererseits sehe ich im Projekt auch nicht den Bedarf nach einer
zweiten Art Config. I(n eine)m Projekt gibt es genau eine Konfiguration,
aus welchen Quellen auch immer. Wenn denn zukünftig die Konfiguration
ganz oder teilweise aus einer Datenbank geholt würde, würde ich die
Config.Klasse eben anpassen. Hauptsache, in den benutzenden Klassen muss
nichts geändert werden. ;-)

> Z.B. würde bei einer Version mit Datenbank der
> Lesethread wegfallen, da ja nicht mehr auf eine Änderung des Property-Files gelauscht
> werden müßte.

Aber holla wäre der noch da, denn auch Datenbankinhalte können sich zur
Laufzeit ändern und einen Refresh der Anwendung nötig machen - und sei
der Thread auch nur da, um regelmäßig alle n Minuten nachzuschauen, ob
sich etwas geändert hat. ;-)

Ich sehe aber Deinen Punkt - es mag eine Quelle geben, bei der kein
Lesethread nötig ist. Nun, dann lösche ich den Code eben. ;-) Die
Aufgabe war eben (leider) nicht, allgemein verwendbare Utilities zu
schreiben, sondern eine konkrete Implementierung im konkreten Projekt
als Wrapper um Properties. Und zwar pronto. ;-)

> > Dann müsste das Ganze also in eine Art nicht-statische
> > initialLoad()-Methode, die nicht automatisch aufgerufen würde. Hmm,
> > macht die Benutzung der Klasse IMHO auch nicht einfacher ... ;-)
>
> Da es reicht, diese init Methode einmal im statischen Initblock aufzurufen,

Ach, als Singelton meinst Du das? Yep, das ginge. IMHO hatte ich das
anfangs auch mal so gemacht. Was mich aber störte, war, dass die
Kollegen mich dann mit Config.CONFIG.getProperty() aufrufen mussten.
Liest sich IMHO nicht so schön. ;-)

> > > > Inwiefern "keine Vererbung"?
> > >
> > > Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
> > > aber nicht überschreiben. Alle Zugriffe erfolgen direkt.
> >
> > Verwechselst Du das mit private-Methoden? Bei mir klappt das
> > Überschreiben statischer Methoden einwandfrei:
>
> Das nennt man verdecken.

Oops! ;-)

> Bei einer Vererbung würde auch dann Erbtest1.staticMethod()
> aufgerufen, wenn der Referenztyp Erbtest ist.

Du könntest nicht mal eben 10 Zeilen ungetesteten Code als Beispiel
zusammenwürfeln? Das wäre supernett.

Bye
Achim


Achim Peters

unread,
Apr 8, 2002, 6:29:00 PM4/8/02
to
Hi Patrick,

> > > Bernds Einwand der Nicht-Debugbarkeit ziemlich schlagend.
> >
> > Ich kann unter VAJ im statischen Initialisierer wunderbar break-pointen
> > und single-steppen - Ihr in Eurer Umgebung nicht?
>
> Schon, aber wer will das?

Einer, der sagt, "Nicht-Debugbarkeit" wäre ein k.o.-Kriterium? ;-)

> Eine aussagekraeftige Exception inkl.
> Stacktrace ist mir weitaus lieber.

Ich schrieb ja, daß wir sowieso alles in try-catch kapseln müssen, weil
wir das System später nicht betreiben und daher für jedes Throwable eine
ordentliche Message her muss. Dank Pauls Erläuterungen in einem früheren
Thread kommt sogar der Stacktrace dabei inzwischen als Message raus. ;-)

> Abgesehen davon handelt es sich hier ja um eine Exception,

"hier"? Welche denn?

> die
> durchaus im Produktivbetrieb vorkommen kann, und da gestaltet
> sich das Debugging wahrscheinlich nicht so komfortabel.

Die Mega-Frage bei Produktionsfehlern ist sowieso meistens, ob man den
Fehler im Test-Environment reproduzieren kann. Wenn nein, hat man auch
mit Stacktrace meistens verloren und muss Debug-Versionen shippen ...
;-)

Ihr habt mich aber überzeugt. Danke an Dich, Karl und bernd für Eure
manchmal etwas strapzierte Geduld! Ich habe jetzt alle init() aus den
statischen Initialisierern rausgenommen, und in "der"
Servlet-init()-Methode (mit sychronized und if (!init) gekapselt)
aufgerufen.

Witzig war noch, daß ich dabei auf eine Null-Pointer-Exception gestossen
bin, die mir bisher in den statischen Initialisierern eben jenes Feature
verhindert hatte, das ich im allerersten Posting ergründen wollte. (Ein
weiterer Thread blieb jetzt eben nicht mehr stehen, bis die abhängige
Klasse vollständig initialisiert war, sondern lief direkt los und ins
Leere ...) ;-)

Bye
Achim

Karl Schmidt

unread,
Apr 8, 2002, 8:36:48 PM4/8/02
to
Achim Peters schrieb:

> > Z.B. würde bei einer Version mit Datenbank der
> > Lesethread wegfallen, da ja nicht mehr auf eine Änderung des Property-Files gelauscht
> > werden müßte.
>
> Aber holla wäre der noch da, denn auch Datenbankinhalte können sich zur
> Laufzeit ändern und einen Refresh der Anwendung nötig machen - und sei
> der Thread auch nur da, um regelmäßig alle n Minuten nachzuschauen, ob
> sich etwas geändert hat. ;-)

Das ist jetzt nicht Dein Ernst, oder?

Ich hätte da eher an ein PreparedStatement in Verbindung mit einem kleinen lokalen Cache
gedacht...

> > Da es reicht, diese init Methode einmal im statischen Initblock aufzurufen,
>
> Ach, als Singelton meinst Du das? Yep, das ginge. IMHO hatte ich das
> anfangs auch mal so gemacht. Was mich aber störte, war, dass die
> Kollegen mich dann mit Config.CONFIG.getProperty() aufrufen mussten.
> Liest sich IMHO nicht so schön. ;-)

Naja, man kann in dem Fall auch locker nochmal eine Referenz in der aufrufenden Klasse
ablegen, wenn's eh dauernd gebraucht wird.

> > > > > Inwiefern "keine Vererbung"?
> > > >
> > > > Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
> > > > aber nicht überschreiben. Alle Zugriffe erfolgen direkt.
> > >
> > > Verwechselst Du das mit private-Methoden? Bei mir klappt das
> > > Überschreiben statischer Methoden einwandfrei:
> >
> > Das nennt man verdecken.
>
> Oops! ;-)
>
> > Bei einer Vererbung würde auch dann Erbtest1.staticMethod()
> > aufgerufen, wenn der Referenztyp Erbtest ist.
>
> Du könntest nicht mal eben 10 Zeilen ungetesteten Code als Beispiel
> zusammenwürfeln? Das wäre supernett.

Ich habe mich vielleicht undeutlich ausgedrückt :-)

Nehmen wir ein einfaches Beispiel:

public class Test1 {
public Test1() {
test();
staticTest();
}
public void test() {
System.out.println("test1");
}
public static void staticTest() {
System.out.println("static test 1");
}
}

public class Test2 extends Test1 {
public Test2() {
test();
staticTest();
}
public void test() {
System.out.println("test2");
}
public static void staticTest() {
System.out.println("static test 2");
}
}


Jetzt rufen wir den Krempel mal auf:

Test1 test1 = new Test1();

ergibt die Ausgabe:

test1
static test 1

soweit so gut. Aber

Test2 test2 = new Test2();

ergibt ein abweichendes Ergebnis. Beachte, daß der Constructor der Superklasse als erstes
durchlaufen wird.

test2
static test 1
test2
static test 2


Und hier hast Du den Unterschied zwischen überschreiben (vererbung) und verdecken. Auch im
Constructor der Superklasse Test1 weiß das Objekt, daß es vom Typ Test2 ist und ruft die
richtige dynamische Methode auf. Die statische Methode wird dagegen nur innerhalb des
Codes von Test2 durch eine neue Methode verdeckt.

Außerhalb der Klassen Test1 und Test2 gibst Du den Klassennamen mit an, wenn Du eine
Methode aufrufen willst. Um die verschiedenen Versionen von staticTest() aufzurufen,
schreibst Du also:

Test1.staticTest();

oder

Test2.staticTest();

die jeweils entsprechend ausgeführt werden.

Dieser Effekt wird verdecken genannt, weil ja auch statische Methoden jeweils zum Namespace
der Subklassen gehören. Wenn Du eine Klasse Test3 hast:

public class Test3 extends Test1 {}

kannst Du jederzeit Test3.staticTest() aufrufen und bekommst die Implementation von Test1.
Bei der Klasse Test4:

public class Test4 extends Test2 {}

wird die Methode verdeckt durch die Implementation von Test2.

Die Sache wird dadurch noch ätzender, daß Du anstelle des Klassennamens auch eine Variable
des Typs nehmen kannst. Du kannst also sagen:

Test1 versuch = new Test1();
versuch.staticTest();

Und hier lauert die bösartigste Falle: Dieser Aufruf orientiert sich nicht am
tatsächlichen Typ der durch "versuch" referenzierten Klasse - ja, er funktioniert sogar,
wenn versuch == null ist - sondern am Typ der Variablen.

Test1 versuch2 = new Test2();
versuch2.staticTest();
versuch2.test();

Bei diesem Beispiel wird die Methode staticTest() aus Test1 aufgerufen, obwohl "versuch2"
ein Test2 Objekt referenziert. Die Ausgabe lautet also:

(hier käme erst die Ausgabe des Constructors)
static test 1
test2


Der einfache Grund liegt darin, daß der Aufruf statischer Methoden bei der Compilierung
eben statisch gelinkt wird. Der Compiler verknüpft den Methodenaufruf mit der Klasse, die
er erkennen kann. Entweder über den mit angegebenen Klassennamen, oder weil er sich
innerhalb des Namespace der betroffenen Klasse befindet.

Deshalb ist das keine "echte" Vererbung. Alle Subklassen haben zwar Zugriff auf die
Methode (Namespace), sie kann jederzeit durch eine mit der gleichen Signatur verdeckt
werden (sieht aus wie Überschreiben), aber die Methode (oder eben statische Felder) sind
nicht eine Eigenschaft eines Objekts und werden nicht zur Laufzeit anhand des tatsächlichen
Objekt-Typs aufgelöst.

Achim Peters

unread,
Apr 10, 2002, 7:17:47 AM4/10/02
to
Hi Karl,

> > > Z.B. würde bei einer Version mit Datenbank der
> > > Lesethread wegfallen, da ja nicht mehr auf eine Änderung des Property-Files gelauscht
> > > werden müßte.
> >
> > Aber holla wäre der noch da, denn auch Datenbankinhalte können sich zur
> > Laufzeit ändern und einen Refresh der Anwendung nötig machen - und sei
> > der Thread auch nur da, um regelmäßig alle n Minuten nachzuschauen, ob
> > sich etwas geändert hat. ;-)
>
> Das ist jetzt nicht Dein Ernst, oder?

Doch. Schon.


>
> Ich hätte da eher an ein PreparedStatement in Verbindung mit einem kleinen lokalen Cache
> gedacht...

Logo. Und? Wie bekommst Du relevante Datenbankänderungen nach
Programmstart mit?

> > > > > > Inwiefern "keine Vererbung"?
> > > > >
> > > > > Statische Methoden und Variablen werden nicht vererbt. Du kannst sie verdecken,
> > > > > aber nicht überschreiben. Alle Zugriffe erfolgen direkt.
>

> Test2 test2 = new Test2();
>
> ergibt ein abweichendes Ergebnis. Beachte, daß der Constructor der Superklasse als erstes
> durchlaufen wird.
>
> test2
> static test 1
> test2
> static test 2
>
> Und hier hast Du den Unterschied zwischen überschreiben (vererbung) und verdecken.

Ok, Tnx!

> Und hier lauert die bösartigste Falle: Dieser Aufruf orientiert sich nicht am
> tatsächlichen Typ der durch "versuch" referenzierten Klasse - ja, er funktioniert sogar,
> wenn versuch == null ist - sondern am Typ der Variablen.
>
> Test1 versuch2 = new Test2();
> versuch2.staticTest();
> versuch2.test();

Ja, ich würde das aber immer als Test1.staticTest() schreiben.

Bye
Achim

Karl Schmidt

unread,
Apr 10, 2002, 10:18:53 AM4/10/02
to
Achim Peters schrieb:

> > Ich hätte da eher an ein PreparedStatement in Verbindung mit einem kleinen lokalen Cache
> > gedacht...
>
> Logo. Und? Wie bekommst Du relevante Datenbankänderungen nach
> Programmstart mit?

Indem die Elemente im Cache einen Timeout verpasst kriegen. Nach xxx ms sind sie nicht mehr
gültig und müssen neu gelesen werden.

Das invalidieren macht mein Cache automatisch. Wenn das Element in den Timeout läuft, wird es
gleich entfernt und null zurückgegeben. Ist die Rückgabe == null, wird das PreparedStatement
"select value from property where key=?" angeworfen. Damit hast Du im Prinzip den gleichen
Effekt wie mit einem extra Thread, der alle xx sekunden das File checked.

Achim Peters

unread,
Apr 10, 2002, 4:57:26 PM4/10/02
to
Hi Karl,

> > > Ich hätte da eher an ein PreparedStatement in Verbindung mit einem kleinen lokalen Cache
> > > gedacht...
> >

> > Wie bekommst Du relevante Datenbankänderungen nach
> > Programmstart mit?
>
> Indem die Elemente im Cache einen Timeout verpasst kriegen. Nach xxx ms sind sie nicht mehr
> gültig und müssen neu gelesen werden.

Und der Timeout ist für verschiedene Elemente unterschiedlich?

> Das invalidieren macht mein Cache automatisch. Wenn das Element in den Timeout läuft, wird es
> gleich entfernt und null zurückgegeben. Ist die Rückgabe == null, wird das PreparedStatement
> "select value from property where key=?" angeworfen. Damit hast Du im Prinzip den gleichen
> Effekt wie mit einem extra Thread, der alle xx sekunden das File checked.

Oft haben verschiedene Property-Werte eine Abhängigkeit zu einander
(IP-Adresse + Port, UserId + Passwort, Strasse + Ort, etc.). Dann wäre
es dumm, wenn bei zwei Abfragen zu einem solchen Paar nach einer
DB-Änderung der Timeout des einen Wertes bereits gerade abgelaufen ist
und der des anderen noch nicht.

Des weiteren erfordert Deine Methode von den Benutzern Deiner Klasse
Polling, wenn sie den Property-Wert lieber in einer eigenen Variable
ablegen, während ein Thread asynchrones notifyAll() ermöglicht.

Soooo abwegig finde ich den eigenen Thread, der auf einen Schlag alle
(geänderten) Werte neu einliest, dazu dann doch nicht. ;-)

Bye
Achim

Karl Schmidt

unread,
Apr 10, 2002, 5:43:45 PM4/10/02
to
Achim Peters schrieb:

> Hi Karl,
>
> > > > Ich hätte da eher an ein PreparedStatement in Verbindung mit einem kleinen lokalen Cache
> > > > gedacht...
> > >
> > > Wie bekommst Du relevante Datenbankänderungen nach
> > > Programmstart mit?
> >
> > Indem die Elemente im Cache einen Timeout verpasst kriegen. Nach xxx ms sind sie nicht mehr
> > gültig und müssen neu gelesen werden.
>
> Und der Timeout ist für verschiedene Elemente unterschiedlich?

Wenn es sein muß. Der Verfallstermin steht im CacheElement. Es ist kein Problem, bestimmten Werten
einen längeren oder kürzeren Timeout zu verpassen.

> > Das invalidieren macht mein Cache automatisch. Wenn das Element in den Timeout läuft, wird es
> > gleich entfernt und null zurückgegeben. Ist die Rückgabe == null, wird das PreparedStatement
> > "select value from property where key=?" angeworfen. Damit hast Du im Prinzip den gleichen
> > Effekt wie mit einem extra Thread, der alle xx sekunden das File checked.
>
> Oft haben verschiedene Property-Werte eine Abhängigkeit zu einander
> (IP-Adresse + Port, UserId + Passwort, Strasse + Ort, etc.). Dann wäre
> es dumm, wenn bei zwei Abfragen zu einem solchen Paar nach einer
> DB-Änderung der Timeout des einen Wertes bereits gerade abgelaufen ist
> und der des anderen noch nicht.

Was Du hier aufzählst klingt mir aber nicht nach typischen allgemeinen Konfigurationsdaten. Aber
das ist halt so. Nicht, daß man das nicht mit irgendwie synchronisieren könnte - oder man führt ein
Flag forceReload ein, wenn man sicherstellen will, daß aktuelle Daten geladen werden...

> Des weiteren erfordert Deine Methode von den Benutzern Deiner Klasse
> Polling, wenn sie den Property-Wert lieber in einer eigenen Variable
> ablegen, während ein Thread asynchrones notifyAll() ermöglicht.

Ich glaube, wir reden hier aneinander vorbei. Wenn Deine Klassen die Properties noch einmal
zusätzlich lokal ablegen, müssen sie selbst MIT extra Thread pollen, um die neuen Daten zu
bekommen. Oder registrierst Du jede einzelne Klasse als eine Art Listener beim Config-Thread?

Achim Peters

unread,
Apr 10, 2002, 6:10:24 PM4/10/02
to
Hi Karl,

> > > Indem die Elemente im Cache einen Timeout verpasst kriegen. Nach xxx ms sind sie nicht mehr
> > > gültig und müssen neu gelesen werden.
>

> > > Das invalidieren macht mein Cache automatisch. Wenn das Element in den Timeout läuft, wird es
> > > gleich entfernt und null zurückgegeben. Ist die Rückgabe == null, wird das PreparedStatement
> > > "select value from property where key=?" angeworfen. Damit hast Du im Prinzip den gleichen
> > > Effekt wie mit einem extra Thread, der alle xx sekunden das File checked.
> >
> > Oft haben verschiedene Property-Werte eine Abhängigkeit zu einander
> > (IP-Adresse + Port, UserId + Passwort, Strasse + Ort, etc.). Dann wäre
> > es dumm, wenn bei zwei Abfragen zu einem solchen Paar nach einer
> > DB-Änderung der Timeout des einen Wertes bereits gerade abgelaufen ist
> > und der des anderen noch nicht.
>
> Was Du hier aufzählst klingt mir aber nicht nach typischen allgemeinen Konfigurationsdaten.

Niemals nicht keine paarweisen Abhängigkeiten irgendwelcher Parameter?

In dem einen unserer aktuellen Projekte (T-Online-RADIUS-Server) gehören
dutzende von IP-Adresse-Port-Paaren zu den (Datenbank-)Konfigurationen
diverser Programme, in dem anderen (Telekom-AfoD-Vertragsverwaltung)
mehrere UserId-Passwort-Paare (DB-User, POP3-User, etc.) im
Property-File. Das dritte Paar habe ich mir als weiteres mögliches
ausgedacht.

Wenn ich mal versuche, mich ganz dumpf an alle Groß-Projekte der letzten
13 Jahre zu erinnern, fallen mir auf Anhieb ausschließlich solche ein,
die mindestens ein solches abhängiges Paar von Daten in der
Konfiguration hatten. Aber was ist schon "typisch allgemein"? ;-)

> > Des weiteren erfordert Deine Methode von den Benutzern Deiner Klasse
> > Polling, wenn sie den Property-Wert lieber in einer eigenen Variable
> > ablegen, während ein Thread asynchrones notifyAll() ermöglicht.
>
> Ich glaube, wir reden hier aneinander vorbei. Wenn Deine Klassen die Properties noch einmal
> zusätzlich lokal ablegen, müssen sie selbst MIT extra Thread pollen, um die neuen Daten zu
> bekommen. Oder registrierst Du jede einzelne Klasse als eine Art Listener beim Config-Thread?

Selbst, wenn nicht, schrieb ich ja "ermöglicht".

In der Tat können sich aber momentan, wie ich das irgendwo weiter oben
IMHO mal nebenbei erwähnte, die Benutzer der Config-Klasse in ein wait()
einklinken, um sich auf Wunsch bei Änderungen per notifyAll() wecken zu
lassen.

Bye
Achim

Karl Schmidt

unread,
Apr 10, 2002, 7:41:05 PM4/10/02
to
Achim Peters schrieb:

> > > Oft haben verschiedene Property-Werte eine Abhängigkeit zu einander
> > > (IP-Adresse + Port, UserId + Passwort, Strasse + Ort, etc.). Dann wäre
> > > es dumm, wenn bei zwei Abfragen zu einem solchen Paar nach einer
> > > DB-Änderung der Timeout des einen Wertes bereits gerade abgelaufen ist
> > > und der des anderen noch nicht.
> >
> > Was Du hier aufzählst klingt mir aber nicht nach typischen allgemeinen Konfigurationsdaten.
>
> Niemals nicht keine paarweisen Abhängigkeiten irgendwelcher Parameter?

Dreh mir nicht das Wort im Mund rum...

> > Ich glaube, wir reden hier aneinander vorbei. Wenn Deine Klassen die Properties noch einmal
> > zusätzlich lokal ablegen, müssen sie selbst MIT extra Thread pollen, um die neuen Daten zu
> > bekommen. Oder registrierst Du jede einzelne Klasse als eine Art Listener beim Config-Thread?
>
> Selbst, wenn nicht, schrieb ich ja "ermöglicht".
>
> In der Tat können sich aber momentan, wie ich das irgendwo weiter oben
> IMHO mal nebenbei erwähnte, die Benutzer der Config-Klasse in ein wait()
> einklinken, um sich auf Wunsch bei Änderungen per notifyAll() wecken zu
> lassen.

Moment mal. Soll jede Klasse einen eigenen Thread losschicken, um auf evtl. Änderungen zu reagieren?
Das wäre ja noch schlimmer als Polling. Produziere ein PropertyChangeEvent, wenn sich was geändert hat
und lass sich die interessierten Klassen als Listener registrieren.

Achim Peters

unread,
Apr 10, 2002, 7:59:15 PM4/10/02
to
Hi Karl,

[Properties einzeln per Timeout invalidieren]


> > > > Oft haben verschiedene Property-Werte eine Abhängigkeit zu einander
> > > > (IP-Adresse + Port, UserId + Passwort, Strasse + Ort, etc.). Dann wäre
> > > > es dumm, wenn bei zwei Abfragen zu einem solchen Paar nach einer
> > > > DB-Änderung der Timeout des einen Wertes bereits gerade abgelaufen ist
> > > > und der des anderen noch nicht.
> > >
> > > Was Du hier aufzählst klingt mir aber nicht nach typischen allgemeinen Konfigurationsdaten.
> >
> > Niemals nicht keine paarweisen Abhängigkeiten irgendwelcher Parameter?
>
> Dreh mir nicht das Wort im Mund rum...

Hatte ich nicht vor. Kommen denn solche paarweisen Abhängigkeiten Deiner
Meinung nach nun typischerweise vor oder nicht?

> > > Ich glaube, wir reden hier aneinander vorbei. Wenn Deine Klassen die Properties noch einmal
> > > zusätzlich lokal ablegen, müssen sie selbst MIT extra Thread pollen, um die neuen Daten zu
> > > bekommen. Oder registrierst Du jede einzelne Klasse als eine Art Listener beim Config-Thread?
> >
> > Selbst, wenn nicht, schrieb ich ja "ermöglicht".
> >
> > In der Tat können sich aber momentan, wie ich das irgendwo weiter oben
> > IMHO mal nebenbei erwähnte, die Benutzer der Config-Klasse in ein wait()
> > einklinken, um sich auf Wunsch bei Änderungen per notifyAll() wecken zu
> > lassen.
>
> Moment mal. Soll jede Klasse einen eigenen Thread losschicken, um auf evtl. Änderungen zu reagieren?

Yep.

> Das wäre ja noch schlimmer als Polling.

Glaube ich nicht. Es bleibt ja jeder Klasse selbst überlassen, ob sie
das macht, und im allgemeinen bleiben die Threads im wait() hängen und
verbrauchen keine Resourcen ausser a bisserl Paging-Space.

> Produziere ein PropertyChangeEvent, wenn sich was geändert hat
> und lass sich die interessierten Klassen als Listener registrieren.

Yep, sobald Du Deine Property-Timeouts abgeschafft hast. ;-)

Nichts für ungut ...

Bye
Achim

Karl Schmidt

unread,
Apr 11, 2002, 5:23:04 AM4/11/02
to
Achim Peters schrieb:

> > Moment mal. Soll jede Klasse einen eigenen Thread losschicken, um auf evtl. Änderungen zu reagieren?
>
> Yep.

Bist Du des Wahnsinns fette Beute?

> > Das wäre ja noch schlimmer als Polling.
>
> Glaube ich nicht. Es bleibt ja jeder Klasse selbst überlassen, ob sie
> das macht, und im allgemeinen bleiben die Threads im wait() hängen und
> verbrauchen keine Resourcen ausser a bisserl Paging-Space.

was ist "a bisserl Paging Space"? Außerdem verbrauchen Deine Threads auch andere Systemressourcen. Die
meisten BS haben z.B. eine maximale Anzahl von Threads, die gleichzeitig existieren können.

> > Produziere ein PropertyChangeEvent, wenn sich was geändert hat
> > und lass sich die interessierten Klassen als Listener registrieren.
>
> Yep, sobald Du Deine Property-Timeouts abgeschafft hast. ;-)

Wenn man es denn unbedingt so machen will, dann kann ich auch mit dem einen pollenden Thread leben, der in
Abständen nach Veränderungen sucht. Besser als 1000 Threads von 1000 Klassen...

Karl Schmidt

unread,
Apr 11, 2002, 5:25:04 AM4/11/02
to
Achim Peters schrieb:

> > Dreh mir nicht das Wort im Mund rum...
>
> Hatte ich nicht vor. Kommen denn solche paarweisen Abhängigkeiten Deiner
> Meinung nach nun typischerweise vor oder nicht?

Ob unbedingt typischerweise, weiß ich nicht - aber sie kommen vor. Ob es aber ein echtes Problem
darstellt, wenn diese dann einen unterschiedlichen Timeout-Wert hätten, bezweifle ich.

0 new messages