Announcement

Collapse
No announcement yet.

Multi-Threading-Problem bei Singleton-Implementierung

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Multi-Threading-Problem bei Singleton-Implementierung

    Hallo,

    habe ein merkwürdiges Phänomen: Habe einen Singleton implementiert, auf den zwei Threads zugreifen. Ein Applet und ein von diesem gestarteter Thread.
    Obwohl ich die bekannten Mechanisman angewandt habe, das Ding threadsave zu machen (siehe http://www.galileocomputing.de/openbook/java2/kap_10.htm, Kap 10.5), wird der Konstruktor zweimal durchlaufen und es werden zwei Objekte instanziert.

    Hat jemand eine Idee, woran das liegen könnte?

    Gruß
    Stefan

  • #2
    Hallo Stefan!

    Kannst du bitte mal dafür ein Beispielcode posten?

    Gruß Rocc

    Comment


    • #3
      Hallo Rocco,

      danke der prompten Nachfrage. Hier die entscheidenden Stellen. Soll ein Wrapper um den LogManager (java.util.logging-API) sein

      public class MyLogManager
      {
      //other members
      private LogManager manager = null;
      private static MyLogManager myManager = null;
      private MyLogManager()
      {
      manager = LogManager.getLogManager();
      ...
      }

      public static MyLogManager getLogManager()
      {
      if (myManager == null)
      {
      synchronized (MyLogManager.class)
      {
      if (myManager == null)
      {
      myManager = new MyLogManager();
      }
      }
      }
      return myManager;
      }
      }

      Sollte doch eigentlich threadsave sein, oder? Die Variante mit der Instanzierung in der Klassen-Definition (Eager-Variante) wirft sogar einen NullPointer. Bin ziemlich ratlos.
      Ach so, aus zwei Klassen, einmal dem Applet und einer vom diesen in einem anderen Thread instanziierten Business-Klasse (POJO) wird jeweils aus static-CodeBlöcken die Methode getLogeManager aufgerufen. Ist das garstig von mir, so etwas zu tun?

      Gruß
      Stefa

      Comment


      • #4
        Hi Stefan,
        private MyLogManager()
        {
        manager = LogManager.getLogManager();
        }
        Das sieht komisch für mich aus. Ich bin der Meinung, die Zeile müsste weg, der private Konstruktor also leer sei
        http://www.winfonet.eu

        Comment


        • #5
          Hi!

          also eigentlich implementiert man eine singleton class wie folgt:

          private final class MyLogManager {
          private static final MyLogManager myManager = LogManager.getLogManager();

          private MyLogManager() {
          ...
          }

          public static MyLogManager getLogManager() {
          return myManager;
          }
          }

          Gruß rocc

          Comment


          • #6
            Hallo,

            es gibt mehrere Möglichkeiten, einen Singleton threadsave zu implementieren. Meine ist die sogenannte "Lazy Initialisation mit DoubleCheck" (siehe link erster Beitrag).
            Ich nehme an, Du meinst die Variante mit der direkten Instanziierung in der Klassendefinition (Eager-Variante).
            Dann muss es allerdings heißen:
            private static final MyLogManager myManager = new MyLogManager(). (Mit LogManager.getManager kommt ja ein java.util.LogManager-Objekt zurück...)

            Ob das final keyword hier etwas ausmacht, muss ich noch recherchieren, kann ich mir aber nicht vorstellen.
            Diese Variante führt allerdings in meiner Implementierung zu einer NullPointerException. Da die Threads offensichtlich nicht synchronisiert werden, kriegt der 2. Thread ein noch nicht intanzieertes myManager-Objekt zurück (was eigentlich nicht sein darf)
            Hat jemand noch eine Idee?

            Gruß
            Stefa

            Comment


            • #7
              Hallo Stefan,

              Double checked locking ist nicht threadsafe und generell etwas gefährlich. Darüber gab es vor einiger Zeit eine Diskussion hier im Forum aufgrund eines JM-Artikels. Es gibt auch eine Menge Material im Internet darüber.

              http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

              http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html

              Gruß,

              Alwi

              Comment


              • #8
                Hallo Alwin,

                danke. Hab gleich mal reingeschmökert. Ok, das war dann wohl nix mit dem Double checked, aber auch laut diesen Artikeln müsste es ja in jedem Fall (unabhängig von irgendwelchen Performance-Überlegungen) funktionieren, wenn man den ganzen static-getter synchronized macht.
                In diesem Fall habe ich das Problem aber auch. Und mit der
                Direktinitialisierungsvariante (die mir in diesem Fall auch das Sympathischste wäre) bekomme ich wie gesagt eine NullPointerException. Hast Du dafür eine Erlärung?

                Gruß
                Stefa

                Comment


                • #9
                  Hi Stefan,

                  Selbst bei der double-checked Variante sind Probleme in der Praxis sehr selten. Ich habe Deine Klasse mal mit mit voll synchronisiertem static und mit direkter Initialisierung gestestet (also aus mehreren Threads getLogManager() aufgerufen) und bekomme keine NullPointerException und es existiert auch nur jeweils eine Instanz. Ohne den kompletten Code kann man an dieser Stelle nicht sagen, was genau da falsch läuft.

                  Gruß,

                  Alwi

                  Comment


                  • #10
                    Hi Alwin,

                    vielen Dank für Deine Mühe!!
                    Hm, der ganze Code wäre wohl ein bisschen viel/und auch bei kommerzieller Software (für/vom Kunden) nicht der richtige Ort für dieses Forum. Da bitte ich um Verständnis. Bin trotzdem sehr interessiert an irgendwelchen Ideen, da ich sogar mit kompletten Code weder genau, noch vage vermuten kann, woran es liegt. ;-) Denn es ist nichts Besonderes, was ich da mache. Ein stinkormales Java-Programm, in dem ich pro Klasse einmal in einem static-Block jeweils einmal auf diesen Singleton (per getter) zugreifen möchte. Das einzige, was mir als Hinweis einfällt, ist, dass es sich dabei um ein Applet handelt, das aber wie eine Java-Applikation gestartet wird (habe ich so übernommen). Das ist mein persönlicher Verdächtiger...

                    Gruß
                    Stefa

                    Comment


                    • #11
                      Hallo, noch ein Nachschlag,

                      mein Hauptverdächtiger scheidet auch aus: Habe das Ding mal auf die schnelle in ein JFrame verwandelt, aber das Problem tritt immer noch auf. Einziger Lichtblick: Es scheint zumindest reproduzierbar zu sein in meiner Umgebung, wenn auch nicht im Debugger. Wenn ich über Eclipse debugge, ist alles i. O. Tritt nur bei Start über Kommandozeile auf. Dort allerdings hartnäckig.

                      Muss noch mal alles durchsehen, wüsste aber eigentlich nicht, was man da überhaupt falsch machen kann, wenn der static getter synchronized ist... :-((

                      Gruß
                      Stefa

                      Comment


                      • #12
                        Darf ich nachfragen wie du deine Implementierung bzw. Singleton aufrufst?

                        mfg roma

                        Comment


                        • #13
                          Hallo Roman,

                          klar, ist wahrlich nichts Aufregendes:
                          static
                          {
                          MyLogManager manager =
                          MyLogManager.getLogManager();
                          (static MyLogger) classLogger = manager.getLogger();
                          }

                          Dies geschieht in der Klassendefinition, da ich das Objekt eh immer und überall brauche. Dies ist auch der einzige Code, der auf diese Klasse zugreift, ansonsten gibt es nichts Relevantes im übrigen Code (außer natürlich den gleichen Zeilen in anderen Klassen).
                          Und die ersten beiden Aufrufe kommen sich hartnäckig in die Quere.

                          Gruß
                          Stefan

                          P.S.:
                          Über das Design als Solches könnte man sicher auch streiten. Habs halt mal so gemacht, aber unabhängig davon dürfte diese Problem m. E. nicht auftauchen.
                          &#10

                          Comment


                          • #14
                            Hallo Stefan,

                            ich würde mal vorschlagen, dass du zum Testen die Verwendung des Singleton erst einmal aus dem static-Block rausnimmst und deine Implemmentierung direkt verwendest. Etwa so:

                            MyLogManager.getLogManager().getLogger().log("sing leton called");

                            Sind danach die Log-Ausgaben identisch?

                            mfg roma

                            Comment


                            • #15
                              Hallo Roman,

                              habe es mal ausprobiert, die Initialisierung meines Loggers in den Konstruktor zu verlegen, obwohl er eindeutig static-Charakter hat. Danach läuft es tatsächlich ohne Probleme. Aber nach meinem Verständnis ist das eigentlich ein workaround...
                              Das bedeutet, dass die Thread-Synchronisierung zum Zeitpunkt des Ladens der Klassen nicht zuverlässig funktioniert. Das ist gut zu wissen und gibt Erfahrungspunkte. Aber i. O. ist das m. E. nicht. Oder sehe ich das falsch? Das hat zur Folge, dass ich in jeder static-Methode, die den Klassenlogger benutzen will, auf null überprüfen muss mit evtl 'Nachladen'. Zum Glück bin ich eh kein Freund von static-Methoden. Aber wenn ich es wäre, würde ich das ziemlich lästig finden... ;-))

                              Danke und Gruß
                              Stefa

                              Comment

                              Working...
                              X