Java Gültigkeit von statischen Variablen in einem Thread

GTR

Lt. Commander
Registriert
Feb. 2006
Beiträge
1.236
Ich habe ein Problem das ich bisher nicht ganz lösen konnte.

Im Prinzip geht es um eine Java Anwendung die Java Applikationen startet und stoppt.
Dabei wird ein neuer Thread/Threadgroup erzeugt die Anwendung darin gestartet und eben wieder gestoppt.

Das funktioniert alles soweit gut nur ist das Problem das alle statischen Variablen belegt bleiben was vor allem für Singletons ein Problem ist. Der GC arbeitet ja nur dann wenn auf ein Objekt keine Reference mehr besteht was bei statischen Singleton Holder natürlich nie der Fall ist.

Gibt es irgendeine Möglichkeit in Java einen Thread so zu starten das alle Variablen die Gültigkeit verlieren sobald dieser Thread terminiert ist?
 
Ich müsste dann aber x tausend Stellen ändern und alle Singletons anpassen?

Wenn ich das richtig verstanden habe ist Threadlocal eine Datenstruktur um eben Variablen zu speichern die nur lokal für den Thread sind?

Was ich suche ist das alle variablen im Thread lokal sind.

Gruß
 
Was meinst du mit "Alle statischen Variablen bleiben belegt". Hast du mal ein Codebeispiel? Was genau ist das Problem?
 
Darkspire schrieb:
Was meinst du mit "Alle statischen Variablen bleiben belegt". Hast du mal ein Codebeispiel? Was genau ist das Problem?

Alle Konstanten behalten ihre werte.

Hier das ist kein Problem der GC wird das objekt aufräumen.
Code:
Object a = new Object();
a = null;


Code:
public class Singleton {
    private Object a;
    private Singleton () {
          a = new Object();
    }
 
    private static class InstanceHolder {
        private static final Singleton  INSTANCE = new Singleton ();
    }
 
    public static Singleton getInstance() {
        return InstanceHolder .INSTANCE;
    }

    public Object getA(){
        return a;
    }

Nehmen wir mal an du verwendest diese Klasse in einem Thread und der Thread terminiert.
Dann hält die statische Variable "INSTANCE" weiterhin eine Reference auf eine Instanz von "Singleton".
Das heißt auch das der Konstruktor nie wieder ausgeführt wird und die Variable a ungültig sein kann.
Greift man nun auf a zu kann es eben krachen sobald der Thread ein zweites mal gestartet wird.
 
GTR schrieb:
Das heißt auch das der Konstruktor nie wieder ausgeführt wird und die Variable a ungültig sein kann.
Der Konstruktor muss ja nicht mehr ausgeführt werden, weil die Instanz (und damit auch a) noch da ist.

GTR schrieb:
Greift man nun auf a zu kann es eben krachen sobald der Thread ein zweites mal gestartet wird.
In wie fern sollte es da krachen? "a" wird durch das Ende des Threads ja nicht GCed, weil dein "Singleton"-Object auch nicht GCed wird (da statisch).

Ich versteh dein Problem nicht ganz. Willst du, dass für jeden Thread eine eigene Singleton-Instanz erzeugt wird?
 
Object a ist ja nur ein beispiel.

Bei den Beispiel geht es nur darum zu zeigen was das Problem ist.
Im praktischen Fall wo es um komplexere Initialisierungen geht läuft es darauf hinaus das "a" auf ein ungültiges Objekt referenziert.

Im praktischen Fall sind es z.b. RMI Objekte die registriert wurden und inzwischen ungültig sind weil z.b. RMIServer.getInstance().start() zwar den Server hochfährt aber nicht alle Objekte neu instanziert (wird im Konstruktor gemacht) und dann eben die alten Referenzen verwendet werden.

Willst du, dass für jeden Thread eine eigene Singleton-Instanz erzeugt wird?
Genau das. Weil ich damit 100% sicher sein kann das eben fehlerhafte Referenzen nicht erhalten bleiben.
Ergänzung ()

Hier mal ein ganz gekürztes Beispiel wo es z.b. kracht wenn der Thread gestartet, gestoppt und wieder gestartet wird:

Code:
private RMIServer()
    {
        init();
    }
    
    public static RMIServer getInstance()
    {
        return RMIServerHolder.INSTANCE;
    }
    
    //Singleton Holder
    private static class RMIServerHolder {
        private static final RMIServer INSTANCE = new RMIServer();
    }
    
    private void init(){
        try {
            rmiObjects = new RMIObjects();
        } catch (RemoteException ex) {
        }
    }
    
    public void start() {
        //init();
        try{
            this.createRegistry();
            rmiObjects.register(this);    
        }
        catch (RemoteException ex){
        }
    }
public void stop() {
        try {
            rmiObjects.unregister(this);
            UnicastRemoteObject.unexportObject(reg, true);
        } catch (NoSuchObjectException ex) {
        }   
    }
Code:
public void register(RMIServer server) {
        server.bindRmiObject(RMINames.myRemote, myRemoteImp);
    }

Es gibt keinen Fehler wenn start() aufgerufen wird wenn der Thread das zweite mal startet. Allerdings sollte man nun über RMI versuchen auf die registrierten Objekte zu zugreifen kommt eine WriteError/Unmarshalling/Not BoundException.
Verlagert man die Initialisierung (init Methode) in den start() Aufruf hat man z.b. das Problem nicht.

Es geht übrigens nicht darum das zwei Threads gleichzeitig laufen können sondern nur darum den Applikationsthread mehrfach stoppen und starten zu können.
(z.b. stop app -> datenbank restore -> start app getriggert über rmi)...
 
Zuletzt bearbeitet:
Habe ich richtig verstanden, dass Du einen Singleton nacheinander in zwei verschiedenen Threads benutzen möchtest, wobei aber der Zustand des Singleton abhängig vom jeweiligen Thread ist?

Ohne das Problem jetzt wirklich durchdrungen zu haben, klingt das für mich nach einem Designfehler: Wenn bspw. der RMIServer threadspezifisch ist, solltest Du ihn auch für jeden Thread separat erzeugen und nicht übergeordnet als Singleton vorhalten. Falls der Singleton wirklich nur sequentiell von den Threads benutzt wird, könntest Du ihn eben auch für jeden Thread neu initialisieren, was ja in Deinem Beispiel anscheinend auch funktioniert. Fände ich von der Architektur her aber auch irgendwie suboptimal.

Eine andere Variante wäre eventuell die Verwendung verschiedener Classloader. Das habe ich selbst noch nie gemacht, aber bspw. arbeitet ja auch Tomcat nach diesem Prinzip, um das Laden von Klassen zwischen mehreren Applikationen auf dem Server zu trennen. Du könntest also in jedem Thread einen eigenen Classloader verwenden - dann würde auch der Singleton in jedem Thread wieder neu erzeugt werden. Das wäre aus meiner Sicht allerdings eine Notlösung.
 
Daß Singletons der letzte Dreck sind (genau wie alle globalen [nicht konstanten] Daten), müßte doch inzwischen wirklich hinreichend bekannt sein. Sieht man ja wieder wunderschön an diesem Beispiel.
 
Prypjat schrieb:
Habe ich richtig verstanden, dass Du einen Singleton nacheinander in zwei verschiedenen Threads benutzen möchtest, wobei aber der Zustand des Singleton abhängig vom jeweiligen Thread ist?

Ohne das Problem jetzt wirklich durchdrungen zu haben, klingt das für mich nach einem Designfehler: Wenn bspw. der RMIServer threadspezifisch ist, solltest Du ihn auch für jeden Thread separat erzeugen und nicht übergeordnet als Singleton vorhalten.

Gebe ich dir vollkommen Recht nur darfst du dabei nicht vergessen:

UseCase
Code:
Daemon()
Applikation1->main()
Applikation2->main()

Daemon()->new Thread(Applikation1->main().start()
Daemon()->new Thread(Applikation2->main().start()
WebServer->RMI->DoDataBaseRestore()
Daemon()->Applikation1->exit()
Daemon()->Applikation2->exit()
Daemon()->RestoreDB()
Daemon()->new Thread(Applikation1->main().start()
Daemon()->new Thread(Applikation2->main().start()

Das heißt das die Applikation1 natürlich unsrprünglich als eingenständige Applikation gedacht war. Und natürlich wurden nur dann Singletons verwendet wenn sie in der gesamten Applikation nur einmal verwendet wurden. z.B. hat die App1 eben nie mehr als einen DatenbankServer oder einen RMIServer.....
Jetzt aber sind alle Singletons aus App1 oder App2 auch für die Daemon JVM gültig.

Jetzt könnte man natürlich das ganze umgehen indem man das anders löst:
Daemon->startNewProcess->Start JVM with parameters
oder noch komplizierter:
Daemon->startService>Windows? Linux? -> start service/start init.d daemon
Das bedeutet aber eben das der Zugriff komplizierter wird inkl des stoppen, status etc der Anwendung...
 
Zuletzt bearbeitet:
antred schrieb:
Daß Singletons der letzte Dreck sind (genau wie alle globalen [nicht konstanten] Daten), müßte doch inzwischen wirklich hinreichend bekannt sein.

Kannst du das bitte begründen (ich gehe bei der Frage natürlich davon aus, dass man Singletons nur dafür einsetzt, wofür sie gedacht sind)? Wie verwaltet beispielsweise sonst Ressourcen, die naturgemäß nur eine "Instanz" haben können?

e: ich habe das Gefühl, dass das Pattern aufgrund der vermeintlichen Vereinfachung für alles Mögliche eingesetzt wird und sich die Leute hinterher wundern, warum sie von einer Sackgasse in die nächste stolpern. Wenn Singletons schlecht sind, dann durch Missbrauch, aber das gilt ja eigentlich für alle Praktiken.
 
Zuletzt bearbeitet:
Wie gesagt es geht hier um einen ganz anderen Fall das eine Applikation eine andere Java APPLIKATION startet und es nicht möglich ist zu verhindern das die variablen geteilt werden.

Es läuft wohl auf ein ProcessBuilder hinaus um eine unabhängige jvm zu starten. Das verkompliziert das ganze etwas dürfte aber die sauberste lösung sein....
 
carom schrieb:
Kannst du das bitte begründen? Ich gehe bei der Frage natürlich davon aus, dass man Singletons nur dafür einsetzt, wofür sie gedacht sind (ob dies beim TS der Fall ist, darf bezweifelt werden).

Klar kann ich. ;)

  1. Singletons sind praktisch von überall her erreichbar. Sie unterscheiden sich in quasi nix von einer globalen Variablen. Beziehungen zwischen Klassen / Objekten werden hier von den Schnittstellen in die Implementierung verschoben und damit verwischt. Bei einer sauberen Architektur würden sich Referenzen auf benötigte Typen / Objekte in den Signaturen von Übergabemethoden / Konstruktoren widerspiegeln. Bei Singletons / Globalen kann einfach jedes Stück Code jederzeit tun und lassen, was es will, ohne daß dies in irgend welchen Schnittstellen ersichtlich wird.
  2. Jeglicher "global state" ist schlecht. Das gilt doppelt und vierfach in nebenläufigen Programmen (also solchen mit mehreren Threads), da ich hier nun jede Verwendung des Singletons mit Mutexen oder ähnlichen Synchronisationsmechanismen schützen muß, entweder seitens des Aufrufers oder gleich direkt in den Methoden der Singletonklasse. Letzteres ist besonders problematisch, da es damit wahrscheinlich zu derart vielen Synchronisationspunkten kommt, daß die Performance des Programm unnötig leidet.
  3. Dieser Punkt trifft wahrscheinlich nur auf C++ zu, da es meines Wissen in Java eh kein RAII gibt. Normale Nicht-Singleton-Objekte während beim Verlassen ihres Gültigkeitsbereiches sauber abgeräumt und die in ihren Destruktoren festgelegten Aufräumarbeiten durchgeführt. Bei Singletons ist man gezwungen, an geeigneter Stelle diverse MySingleton::deinit()-Aufrufe einzufügen ... und wehe, irgend wer hat das verdammte Ding zu diesem Zeitpunkt doch noch in Verwendung!

Das sind nur ein paar Dinge, die mir im Moment einfallen.
 
Der Vorteil von Singletons ist aber die bessere Kapselungsmöglichkeit. Wenn du ständig überall Variablen durchreichst wirst du kaum noch Möglichkeiten der Wiederverwendbarkeit haben.
Die Gefahr einer Spaghettiarchitektur besteht dann umso mehr.
 
GTR schrieb:
Der Vorteil von Singletons ist aber die bessere Kapselungsmöglichkeit. Wenn du ständig überall Variablen durchreichst wirst du kaum noch Möglichkeiten der Wiederverwendbarkeit haben.
Die Gefahr einer Spaghettiarchitektur besteht dann umso mehr.

Code, der wiederverwendbar sein soll, muß das absolute geringst mögliche Maß externer Abhängigkeiten haben. Deshalb ist prinzipiell überhaupt nur solcher Code wiederverwendbar, der praktisch wie eine Library geschrieben wurde. Sobald dein Code irgend welche sehr spezifischen Typen referenziert - egal ob die als Singletons in der Implementierung versteckt sind oder als Übergabeparameter in Funktionsignaturen auftauchen - ist's Essig mit der Wiederverwendbarkeit.
Dieses Kriterium ist für den Großteil des Codes eines Programms von vornherein nicht erfüllt.

Wenn du einige Codeschnipsel in deinem Programm gefunden hast, von denen du meinst, daß man sie prinzipiell häufiger brauchen könnte und deshalb in eine Library auslagern sollte, dann mußt du diese Codeschnipsel erst mal der Art anpassen, daß entweder sämtliche Typ-Abhängigkeiten verschwinden, ODER aber diese Typ-Abhängigkeiten konfigurierbar werden ... das heißt, der Benutzer kann sie von außen spezifizieren (entweder, indem er eine Klasse von einer von dir definierten Basisklasse ableitet und dann spezifisches Verhalten über eine virtuelle Methode festlegt, oder indem er den Typ als Template-Argument angibt. Ein solche Umstrukturierung sollte wesentlich einfacher sein, wenn sich Abhängigkeiten ohnehin schon in Schnittstellen widerspiegeln als wenn sie irgendwo tief in den Innereien der Implementierung vergraben liegen.

Vor dir bin ich noch nie auf einen Programmierer getroffen, der die These vertrat, Singletons würden Codewiederverwendbarkeit fördern.
Ergänzung ()

carom schrieb:
Wie verwaltet beispielsweise sonst Ressourcen, die naturgemäß nur eine "Instanz" haben können?

Sorry, den Teil hatte ich übersehen. Wie wär's hiermit? Du legst eben nur eine Instanz der Klasse an und reichst diese an alle Nutzer durch! ;)
Diese "Es kann eh nur eine Instanz von dem Ding geben; also mach ich einen Singleton draus"-Denke hat schon bei vielen Programmieren später zu Kopfschmerzen geführt, als sich dann später raustellte, daß es aber z.B. eben doch mehrere Renderer geben kann.
 
Zuletzt bearbeitet:
antred schrieb:
Sorry, den Teil hatte ich übersehen. Wie wär's hiermit? Du legst eben nur eine Instanz der Klasse an und reichst diese an alle Nutzer durch! ;)
Diese "Es kann eh nur eine Instanz von dem Ding geben; also mach ich einen Singleton draus"-Denke hat schon bei vielen Programmieren später zu Kopfschmerzen geführt, als sich dann später raustellte, daß es aber z.B. eben doch mehrere Renderer geben kann.

Nehmen wir mal an es geht hier um ein Programm mit 20.000.000 Lines und einem 20 köpfigen Entwicklerteam. Und nun will man verhindern das irgendjemand mehr als eine Instanz erzeugt.
Was ist hier nahe liegender als static final und dann wären wir schon wieder beim Singleton.
Ich mache eine Klasse abstract weil ich damit verhindere das die Klasse instanziert wird.
Ich schreibe keinen Kommentar //bitte nicht instanzieren...
Ein Singleton sichert auch dementsprechend davor ab das Fehler gemacht werden.


Apropos Processbuilder.... natürlich funktioniert Process.destroy() nur unter Linux (SIGTERM) und unter Windows wird kein Shutdownhook getriggert sondern der Prozess gnadenlos gekillt.... und da hätten wir das nächste Problem...
 
GTR schrieb:
Nehmen wir mal an es geht hier um ein Programm mit 20.000.000 Lines und einem 20 köpfigen Entwicklerteam. Und nun will man verhindern das irgendjemand mehr als eine Instanz erzeugt.

Das erkaufst du dir aber zum Preis all der anderen heftigen Nachteile, die ich oben genannt habe. Ich halte es für deutlich besser, den Entwicklern klar zu machen, daß die betroffene Klasse eben nicht nach Belieben neu-instanziiert werden darf, sondern daß gefälligst das per Referenz / Pointer durchgereichte Objekt zu verwenden ist. Wenn man das klar kommuniziert und per Kommentar in der Definition der Klasse selbst deutlich macht, sehe ich da kein Problem.
Eine gewisse Grundkenntnis des Systems muß man bei seinen Entwicklern schon voraussetzen können, sonst kommt da am Ende eh nur Bockmist raus.


P.S. Je nach eingesetzter Sprache könnte ich mir noch diverse Mittel vorstellen, um ein versehentliches Instanziieren unmöglich oder zumindest sehr unwahrscheinlich zu machen.
 
Zuletzt bearbeitet:
Bin mir nicht sicher, ob ich die Bedürfnisse richtig verstanden habe, aber ist hier nicht IoC gesucht? Man instantiiert die entsprechenden Klassen nicht händisch, sondern benutzt z.B. Spring und lässt Spring die Instanz als Bean erstellen, die dann nur per dependency injection in die Klassen geschoben wird, die sie brauchen.

Singletons sind schon wegen Testbarkeit pfui.
 
Da gebe ich Tumbleweed vollkommen Recht. Am saubersten wäre hier ein IoC-Container wie der ApplicationContext von Spring. Seit Version 3.0 gibt es dort übrigens einen Thread-Scope für Beans, bei dem eben jeder Thread seine eigene Instanz aus dem Context erhält (siehe http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/context/support/SimpleThreadScope.html - muss man allerdings als Custom Scope registrieren.

Jedenfalls sind Singletons in diesem Szenario nicht mehr das richtige Pattern, weil offenbar die beiden Applikationen dieselben Singleton-Klassen verwenden, deren Zustand aber unter den Anwendungen inkompatibel ist. Es gibt aus meiner Sicht drei Lösungsvarianten:

1. Refactoring der Singletons, so dass diese zu anwendungsspezifischen Beans werden (z.B. mit Hilfe von Spring) - würde ich empfehlen.
2. Jede Anwendung in einem eigenen Classloader ausführen - dann können die Singletons bleiben und werden für jede Applikation separat erzeugt (evtl. kann man hierbei http://classworlds.codehaus.org/ zu Hilfe nehmen)
3. Die Anwendungen als externe Java-Prozesse starten, was allerdings mehr Arbeitsspeicher verbraucht, weil jeweils eine komplett separate VM hochgefahren werden muss. - wäre vermutlich die schnellste Lösung.
 
Zurück
Oben