2 Stimmen

Zugriff auf globale Variable in einem mehrfädigen Tomcat-Server

Bearbeiten: Ich habe herausgefunden, dass der Konstruktor für das Singleton mehrmals aufgerufen wird, sodass anscheinend die Klassen mehrmals von separaten Klassenladern geladen werden. Wie kann ich ein globales Singleton in Tomcat erstellen? Ich habe gegoogelt, aber bisher kein Glück gehabt.

Ich habe ein Singleton-Objekt, das ich folgendermaßen konstruiere:

private static volatile KeyMapper mapper = null;

public static KeyMapper getMapper()
{
    if(mapper == null)
    {
        synchronized(Utils.class)
        {
            if(mapper == null)
            {
                mapper = new LocalMemoryMapper();
            }
        }
    }

    return mapper;
}

Die Klasse KeyMapper ist im Grunde eine synchronisierte Hülle für HashMap mit nur zwei Funktionen, eine zum Hinzufügen einer Zuordnung und eine zum Entfernen einer Zuordnung. Wenn ich auf meinem 32-Bit-Windows-Computer unter Tomcat 6.24 laufe, funktioniert alles gut. Wenn ich jedoch auf einem 64-Bit-Linux-Computer (CentOS 5.4 mit OpenJDK 1.6.0-b09) laufe, füge ich eine Zuordnung hinzu und drucke die Größe der von KeyMapper verwendeten HashMap, um zu bestätigen, dass die Zuordnung hinzugefügt wurde (d.h. bestätigen Größe = 1). Dann versuche ich, die Zuordnung mit einer anderen Anfrage abzurufen, und ich erhalte immer null und als ich die Größe der HashMap überprüfte, war sie 0. Ich bin zuversichtlich, dass die Zuordnung nicht versehentlich entfernt wird, da ich alle Aufrufe von remove auskommentiert habe (und ich verwende weder clear noch andere Veränderer, nur get und put).

Die Anfragen werden über Tomcat 6.24 (konfiguriert für die Verwendung von 200 Threads mit mindestens 4 Threads) durchgeführt und ich habe -Xnoclassgc an die JVM übergeben, um sicherzustellen, dass die Klasse nicht versehentlich vom Garbage Collector eingesammelt wird (die JVM läuft auch im -server-Modus). Ich habe auch eine finalize Methode zu KeyMapper hinzugefügt, um zu drucken, wenn es jemals vom Garbage Collector eingesammelt wird, um zu bestätigen, dass dies nicht der Fall war.

Ich bin mit meinem Latein am Ende und kann nicht verstehen, warum einmal der Eintrag in der HashMap vorhanden ist und dann nicht mehr :(

5voto

Sean Owen Punkte 64909

Noch eine wilde Vermutung: Ist es möglich, dass die beiden Anfragen von verschiedenen Kopien Ihrer Web-App bedient werden? Jede würde in ihrem eigenen ClassLoader sein und hätte somit eine andere Kopie des Singleton.

1voto

djna Punkte 53789

Hast du versucht, die äußere Überprüfung zu entfernen?

if(mapper == null)
{

Dadurch wird immer der synchronisierte Punkt erreicht. Es ist subtile Sache, aber möglicherweise triffst du auf das Problem des doppelt überprüften Sperrmechanismus. Beschrieben hier und in vielen anderen Artikeln.

Ich muss zugeben, dass ich das Problem noch nie bei jemandem gesehen habe, aber das klingt sicher danach.

1voto

Espen Punkte 10195

Mit dieser Lösung garantiert die JVM, dass es nur einen Mapper gibt und dass er vor der Verwendung initialisiert wird.

public enum KeyMapperFactory {

    ;

    private static KeyMapper mapper = new LocalMemoryMapper();  

    public static KeyMapper getMapper() {
        return mapper;
    }
}

1voto

ZZ Coder Punkte 72742

Dies ist möglicherweise nicht die Ursache für Ihr Problem, aber Sie verwenden das fehlerhafte Double-Checked-Locking. Sehen Sie sich dies an,

http://de.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java

0voto

jwegan Punkte 363

Ich fand einen ziemlich schlechten Fix. Ich exportierte meinen Code als JAR und legte ihn in $TOMCAT/lib und das hat funktioniert. Das ist offensichtlich ein Klassenladeproblem.

Bearbeitet: Lösung gefunden

Ok, ich habe das Problem schließlich gelöst.

Ich hatte meine Anwendung als Standardanwendung für den Server festgelegt, indem ich zu server.xml hinzugefügt und den Pfad auf "" gesetzt habe. Jedoch, als ich darauf über die URL http://localhost/somepage.jsp für manche Dinge zugriff, aber auch die URL http://localhost/appname/anotherpage.jsp für andere Dinge.

Als ich alle URLs geändert habe, um http://localhost/ anstelle von http://localhost/appname zu verwenden, wurde das Problem behoben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X