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
}