1. Was ist das Singleton-Muster
Das Singleton-Muster ist ein Erzeugungsmuster, das sicherstellt, dass eine Klasse nur eine Instanz hat und einen globalen Zugriffspunkt für den Zugriff auf diese Instanz bietet. Das Singleton-Muster wird häufig in Szenarien verwendet, in denen Ressourcen gemeinsam genutzt werden müssen oder der Zugriff auf eine bestimmte Instanz kontrolliert werden muss.
2. Merkmale und Vorteile des Singleton-Musters
Das Singleton-Muster hat folgende Merkmale und Vorteile:
- Stellt sicher, dass die Klasse nur ein Instanzobjekt hat
- Bietet einen globalen Zugriffspunkt für externen Code, um die Instanz zu erhalten
- Vermeidet wiederholte Erstellung von Instanzen und spart damit Systemressourcen
3. Anwendungsszenarien des Singleton-Musters
Das Singleton-Muster eignet sich für die folgenden Anwendungsszenarien:
- Protokollierung: Sicherstellen, dass es nur einen Logger für das gesamte System gibt, um doppelte Protokollierung zu verhindern.
- Datenbankverbindungspool: In Umgebungen mit hoher Konkurrenz kann durch Verwendung des Singleton-Musters die häufige Erstellung und Zerstörung von Datenbankverbindungen vermieden werden.
4. Implementierung des Singleton-Musters in Golang
In Golang gibt es verschiedene Möglichkeiten, das Singleton-Muster zu implementieren. Nachfolgend sind zwei gängige Implementierungsmethoden aufgeführt.
4.1. Implementierung unter Verwendung von Lazy Initialization und Eager Initialization
Die Lazy-Initialisierung erstellt das Instanzobjekt, wenn es zum ersten Mal verwendet wird, während die Eager Initialisierung das Instanzobjekt erstellt, wenn das Programm startet.
// Implementierung des Singleton-Musters mit Lazy-Initialization
package singleton
type Singleton struct {
}
var instance *Singleton
func GetInstance() *Singleton {
if instance == nil {
instance = &Singleton{}
}
return instance
}
// Implementierung des Singleton-Musters mit Eager-Initialization
package singleton
type Singleton struct {
}
var instance *Singleton = &Singleton{}
func GetInstance() *Singleton {
return instance
}
4.2. Thread-Sicherheitsprobleme im Singleton-Muster
Die oben genannte Implementierungsmethode der Lazy-Initialisierung kann in einer Mehrkernumgebung Probleme verursachen, da mehrere Threads gleichzeitig die if instance == nil
-Bedingung durchlaufen und so die Erstellung mehrerer Instanzen verursachen könnten.
4.3. Implementierung von Thread-sicheren Singleton-Muster unter Verwendung von sync.Once
Die Verwendung von sync.Once
stellt sicher, dass nur eine Goroutine den Initialisierungscode ausführt und damit das Thread-Sicherheitsproblem löst.
// Implementierung des thread-sicheren Singleton-Musters unter Verwendung von sync.Once
package singleton
import (
"sync"
)
type Singleton struct {
}
var instance *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
4.4. Implementierung des thread-sicheren Lazy-Initialization-Singleton-Musters unter Verwendung von sync.Mutex
Eine andere Möglichkeit, das thread-sichere Lazy-Initialization-Singleton-Muster zu implementieren, besteht darin, sync.Mutex
zu verwenden, um eine Sperre zu setzen und sicherzustellen, dass nur eine Goroutine die Initialisierungsoperation ausführt.
// Implementierung des thread-sicheren Lazy-Initialization-Singleton-Musters unter Verwendung von sync.Mutex
package singleton
import (
"sync"
)
type Singleton struct {
}
var instance *Singleton
var mu sync.Mutex
func GetInstance() *Singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &Singleton{}
}
}
return instance
}