Entwurfsmuster und Frameworks Singleton Oliver Haase Oliver Haase Emfra Singleton 1/20
Beschreibung I Klassifikation: objektbasiertes Erzeugungsmuster Zweck: sicherstellen, dass eine Klasse nur genau einmal instanziiert wird globalen Zugriffspunkt auf einzige Instanz bereitstellen Anwendungsbeispiele: Genau ein Treiber für ein Stück Hardware (Drucker) Ein Socketlistener pro Server, der alle Anfragen entgegennimmt Applikation, die nur einmal pro Rechner gestartet werden darf globale Event Warteschlange bei der Discrete-Event-Simulation Oliver Haase Emfra Singleton 2/20
Beschreibung II Struktur: Teilnehmer: Singleton zuständig für Erzeugung der einzigen Instanz stellt statische Operation für Zugriff auf diese Instanz bereit Interaktionen: Klienten greifen auf Singleton Instanz nur über getinstance Operation zu Oliver Haase Emfra Singleton 3/20
Beschreibung III Alternative: Klasse mit statischen Variablen und statischen Methoden Nachteile: Oliver Haase Emfra Singleton 4/20
Beschreibung III Alternative: Klasse mit statischen Variablen und statischen Methoden Nachteile: Java Schnittstellen können keine statischen Methoden enthalten unmöglich, Klasse hinter Schnittstelle zu verstecken Jede Methodenbenutzung enthält Klassennamen Polymorphismus ausgehebelt Oliver Haase Emfra Singleton 4/20
Implementierung Oliver Haase Emfra Singleton 5/20
Implementierung Singleton Implementierung mit Lazy Instantiation 1 p u b l i c c l a s s MySingleton { 2 p r i v a t e s t a t i c MySingleton i n s t a n c e = n u l l ; 3 p r i v a t e MySingleton ( ) { 4... 5 6 p u b l i c s t a t i c MySingleton g e t I n s t a n c e ( ) { 7 i f ( i n s t a n c e == n u l l ) { 8 i n s t a n c e = new MySingleton ( ) ; 9 10 r e t u r n i n s t a n c e ; 11 12... 13 Oliver Haase Emfra Singleton 5/20
Implementierung Singleton Implementierung mit Lazy Instantiation 1 p u b l i c c l a s s MySingleton { 2 p r i v a t e s t a t i c MySingleton i n s t a n c e = n u l l ; 3 p r i v a t e MySingleton ( ) { 4... 5 6 p u b l i c s t a t i c MySingleton g e t I n s t a n c e ( ) { 7 i f ( i n s t a n c e == n u l l ) { 8 i n s t a n c e = new MySingleton ( ) ; 9 10 r e t u r n i n s t a n c e ; 11 12... 13 Problem: Kann zur Erzeugung mehrerer Instanzen führen, wenn Thread zwischen Zeile 7 und 8 unterbrochen wird nicht threadsicher! Oliver Haase Emfra Singleton 5/20
Implementierung Behebung des Nebenläufigkeitsproblems durch Synchronisierung der statischen getinstance Methode: p u b l i c c l a s s MySingleton { p r i v a t e s t a t i c MySingleton i n s t a n c e = n u l l ; p r i v a t e MySingleton ( ) {... p u b l i c s t a t i c synchronized MySingleton g e t I n s t a n c e ( ) { i f ( i n s t a n c e == n u l l ) { i n s t a n c e = new MySingleton ( ) ; r e t u r n i n s t a n c e ;... Oliver Haase Emfra Singleton 6/20
Implementierung Problem: Nur erster Aufruf von getinstance müsste synchronisiert werden, alle weiteren nicht: unnötige Reduzierung des Parallelitätsgrades synchronisierte Methoden generell langsamer als nicht-synchronisierte Methoden: Testprogramm mit 10 9 sequentiellen Aufrufen von getinstance auf Mac Mini, 2 GHz Intel Core 2 Duo, 1 GB RAM, 120 GB Harddisc: nicht-synchronisiert synchronisiert 4 sec. 58 sec. Oliver Haase Emfra Singleton 7/20
Implementierung Implementierung mit Checked Locking 1 p u b l i c c l a s s MySingleton { 2 p r i v a t e s t a t i c MySingleton i n s t a n c e = n u l l ; 3 p r i v a t e MySingleton ( ) { 4... 5 6 p u b l i c s t a t i c MySingleton g e t I n s t a n c e ( ) { 7 i f ( i n s t a n c e == n u l l ) { 8 synchronized ( MySingleton. c l a s s ) { 9 i n s t a n c e = new MySingleton ( ) ; 10 11 12 r e t u r n i n s t a n c e ; 13 14... 15 Oliver Haase Emfra Singleton 8/20
Implementierung Szenario: Thread 1 wird zwischen 8 und 9 unterbrochen hält Sperre an MySingleton Klassenobjekt, noch kein Singleton-Objekt instanziiert. Thread 2 durchläuft 7, muss vor 8 auf Thread 1 warten Thread 1 läuft zu Ende, erzeugt Singleton Objekt Thread 2 läuft zu Ende, erzeugt zweites Singleton Objekt nicht threadsafe! Oliver Haase Emfra Singleton 9/20
Implementierung Lösungsversuch über Double-Checked-Locking-(Anti-)Pattern: 1 p u b l i c c l a s s MySingleton { 2 p r i v a t e s t a t i c MySingleton i n s t a n c e = n u l l ; 3 p r i v a t e MySingleton ( ) { 4... 5 6 p u b l i c s t a t i c MySingleton g e t I n s t a n c e ( ) { 7 i f ( i n s t a n c e == n u l l ) { 8 synchronized ( MySingleton. c l a s s ) { 9 i f ( i n s t a n c e == n u l l ) { 10 i n s t a n c e = new MySingleton ( ) ; 11 12 13 14 r e t u r n i n s t a n c e ; 15 16... 17 Funktioniert oder? Oliver Haase Emfra Singleton 10/20
Implementierung Problem: Threadumschalten passiert in Java Bytecode, nicht auf Java Quellcode Ebene... Objekterzeugung in Zeile 10 i n s t a n c e = new MySingleton ( ) ; wird abgebildet auf folgenden Pseudo Bytecode 1 ptrmemory = allocatememory ( ) 2 assignmemory ( i n s t a n c e, ptrmemory ) 3 c a l l M y S i n g l e t o n C o n s t r u c t o r ( i n s t a n c e ) Umschalten zwischen Zeile 2 und Zeile 3 bewirkt, dass instance!= null, obwohl Objekt noch nicht richtig initialisiert Thread 2 kann uninitialisiertes Objekt instance zurückliefern, wenn Thread 1 innerhalb von Aufruf 10 unterbrochen wird! Oliver Haase Emfra Singleton 11/20
Implementierung Die einfachen Dinge sind oft die besten oder: Life can be that simple! funktionstüchtige, performante Lösung: p u b l i c c l a s s MySingleton { p r i v a t e s t a t i c MySingleton i n s t a n c e = new MySingleton ( ) ; p r i v a t e MySingleton ( ) {... p u b l i c s t a t i c MySingleton g e t I n s t a n c e ( ) { r e t u r n i n s t a n c e ;... Oliver Haase Emfra Singleton 12/20
Implementierung Konsequenzen der einfachen Implementierung Oliver Haase Emfra Singleton 13/20
Implementierung Konsequenzen der einfachen Implementierung Singleton-Instanz wird (genau 1x) beim Klassenladen erzeugt auch dann, wenn sie nie benötigt wird aber: passiert selten (das Laden wird i.a. durch Benutzung getriggert) Schaden nicht groß Zeitbedarf beim Laden statt bei erster Benutzung weder besser noch schlechter, nur anders... potentieller Nachteil: Klassenladereihenfolge nicht vorhersehbar Problem, wenn z.b. eine Singleton-Instanz zur Erzeugung einer anderen benötigt wird Oliver Haase Emfra Singleton 13/20
Verwendung Programmiersprache Joi 1 : unterstützt das Singleton-Muster direkt: s i n g l e t o n component MyComponent p r o v i d e s M y I n t e r f a c e {... In der derzeitigen Implementierung wird der Joi-Code auf entsprechenden Java-Code abgebildet. Grund: Joi kennt keine statischen Komponenten statische Variablen werden als Komponenten von Singleton-Klassen modelliert häufige Verwendung des Singleton-Musters 1 Informationen unter http://www-home.htwg-konstanz.de/ haase/hp/joi.html und in [von Drachenfels, Haase, Walter. Joi - eine Java Spracherweiterung zur Reduzierung von Codeabhängigkeiten. In HTWG Forum, 2008/2009] Oliver Haase Emfra Singleton 14/20
Verwendung Java Webstart: Technologie zum dynamischen Herunterladen, Ausführen und Aktualisieren von Applikationen Oliver Haase Emfra Singleton 15/20
Singleton Weiterführendes Manchmal muss das Singleton Objekt bei der Erzeugung (bzw. vor der ersten Verwendung) konfiguriert werden Beispiel: Socket-Listener, der auf bestimmtem Port hören soll Mögliche Realisierungen: getinstance Methode mit Parameter versehen: p u b l i c c l a s s CS1 { p r i v a t e s t a t i c CS1 i n s t a n c e = new CS1 ( ) ; p r i v a t e i n t s t a t e ; p r i v a t e CS1 ( ) { p r i v a t e void c o n f i g ( i n t s t a t e ) { t h i s. s t a t e = s t a t e ; p u b l i c s t a t i c CS1 g e t I n s t a n c e ( i n t s t a t e ) { i n s t a n c e. c o n f i g ( s t a t e ) ; r e t u r n i n s t a n c e ; Parameter muss auch bei Folgeaufrufen angegeben werden Oliver Haase Emfra Singleton 16/20
Singleton Weiterführendes zwei überladene getinstance Methoden mit und ohne Parameter p u b l i c c l a s s CS2 { p r i v a t e s t a t i c CS2 i n s t a n c e = new CS1 ( ) ; p r i v a t e i n t s t a t e ; p r i v a t e CS2 ( ) { p r i v a t e void c o n f i g ( i n t s t a t e ) { t h i s. s t a t e = s t a t e ; p u b l i c s t a t i c CS2 g e t I n s t a n c e ( i n t s t a t e ) { i n s t a n c e. c o n f i g ( s t a t e ) ; r e t u r n i n s t a n c e ; p u b l i c s t a t i c CS2 g e t I n s t a n c e ( ) { r e t u r n i n s t a n c e ; Oliver Haase Emfra Singleton 17/20
Singleton Weiterführendes getinstance Methode ohne Parameter + separate config Instanzmethode, ggf. mehrmaligen Aufruf von config verbieten p u b l i c c l a s s CS3 { p r i v a t e s t a t i c CS3 i n s t a n c e = new CS3 ( ) ; p r i v a t e i n t s t a t e ; p r i v a t e boolean c o n f i g u r e d = f a l s e ; p r i v a t e CS3 ( ) { p u b l i c synchronized void c o n f i g ( i n t s t a t e ) throws E x c e p t i o n { i f ( c o n f i g u r e d ) throw new E x c e p t i o n ( a l r e a d y c o n f i g u r e d ) ; t h i s. s t a t e = s t a t e ; t h i s. c o n f i g u r e d = true ; p u b l i c s t a t i c CS3 g e t I n s t a n c e ( ) { r e t u r n i n s t a n c e ; Oliver Haase Emfra Singleton 18/20
Singleton Weiterführendes getinstance Methode ohne Parameter + separate statische config Methode, ggf. mehrmaligen Aufruf von config und/oder Aufruf von getinstance vor config verbieten p u b l i c c l a s s CS4 { p r i v a t e s t a t i c CS4 i n s t a n c e = new CS4 ( ) ; p r i v a t e i n t s t a t e ; p r i v a t e s t a t i c boolean c o n f i g u r e d = f a l s e ; p r i v a t e CS4 ( ) { Oliver Haase Emfra Singleton 19/20
Singleton Weiterführendes p u b l i c s t a t i c synchronized void c o n f i g ( i n t s t a t e ) throws E x c e p t i o n { i f ( c o n f i g u r e d ) { throw new E x c e p t i o n ( a l r e a d y c o n f i g u r e d ) ; i n s t a n c e. s t a t e = s t a t e ; c o n f i g u r e d = true ; p u b l i c s t a t i c CS4 g e t I n s t a n c e ( ) throws E x c e p t i o n { i f (! c o n f i g u r e d ) throw new E x c e p t i o n ( not c o n f i g u r e d y e t ) ; r e t u r n i n s t a n c e ; Oliver Haase Emfra Singleton 20/20