Was ist ein Singleton in Java?
Singleton ist eines der einfachsten Entwurfsmuster auf Klassenebene. Manchmal sagen die Leute „Diese Klasse ist Singleton“, was bedeutet, dass die Klasse das Singleton-Entwurfsmuster implementiert. Manchmal ist es notwendig, eine Klasse zu schreiben, bei der wir die Instanziierung auf ein einzelnes Objekt beschränken. Zum Beispiel eine Klasse, die für die Protokollierung oder die Verbindung zu einem verantwortlich ist Datenbank. Das Singleton-Entwurfsmuster beschreibt, wie wir dies erreichen können. Ein Singleton ist ein Entwurfsmuster, das zwei Dinge tut:-
Es garantiert, dass es immer nur eine Instanz der Klasse gibt.
-
Es bietet einen einzigen globalen Zugriffspunkt auf diese Instanz.
-
Ein privater Konstrukteur. Dies schränkt die Möglichkeit ein, Objekte der Klasse außerhalb der Klasse selbst zu erstellen.
-
Eine öffentliche statische Methode, die die Instanz der Klasse zurückgibt. Diese Methode heißt getInstance . Dies ist der Punkt des globalen Zugriffs auf die Klasseninstanz.
Umsetzungsmöglichkeiten
Das Singleton-Entwurfsmuster wird auf verschiedene Arten angewendet. Jede Option ist auf ihre Art gut und schlecht. Wie immer gibt es hier keine perfekte Option, aber wir sollten eine anstreben. Lassen Sie uns zunächst entscheiden, was gut und schlecht ist und welche Metriken Einfluss darauf haben, wie wir die verschiedenen Implementierungen des Entwurfsmusters bewerten. Beginnen wir mit dem Guten. Hier sind Faktoren, die eine Implementierung saftiger und ansprechender machen:-
Verzögerte Initialisierung: Die Instanz wird erst erstellt, wenn sie benötigt wird.
-
Einfacher und transparenter Code: Diese Metrik ist natürlich subjektiv, aber wichtig.
-
Thread-Sicherheit: Korrekter Betrieb in einer Multithread-Umgebung.
-
Hohe Leistung in einer Multithread-Umgebung: geringe oder keine Thread-Blockierung beim Teilen einer Ressource.
-
Keine verzögerte Initialisierung: Wenn die Klasse geladen wird, wenn die Anwendung gestartet wird, unabhängig davon, ob sie benötigt wird oder nicht (paradoxerweise ist es in der IT-Welt besser, faul zu sein)
-
Komplexer und schwer lesbarer Code. Auch diese Metrik ist subjektiv. Wenn Ihre Augen anfangen zu bluten, gehen wir davon aus, dass die Umsetzung nicht die beste ist.
-
Mangelnde Thread-Sicherheit. Mit anderen Worten: „Thread-Gefahr“. Falscher Vorgang in einer Multithread-Umgebung.
-
Schlechte Leistung in einer Multithread-Umgebung: Threads blockieren sich ständig oder häufig gegenseitig, wenn sie eine Ressource gemeinsam nutzen.
Code
Jetzt sind wir bereit, verschiedene Implementierungsoptionen zu prüfen und die Vor- und Nachteile aufzuzeigen:Einfach
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
Die einfachste Implementierung. Vorteile:
-
Einfacher und transparenter Code
-
Thread-Sicherheit
-
Hohe Leistung in einer Multithread-Umgebung
- Keine verzögerte Initialisierung.
Verzögerte Initialisierung
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Vorteile:
-
Verzögerte Initialisierung.
-
Nicht Thread-sicher
Synchronisierter Zugriff
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Vorteile:
-
Verzögerte Initialisierung.
-
Thread-Sicherheit
-
Schlechte Multithread-Leistung
Doppelt überprüfte Verriegelung
public class Singleton {
private static final Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Vorteile:
-
Verzögerte Initialisierung.
-
Thread-Sicherheit
-
Hohe Leistung in einer Multithread-Umgebung
-
Wird in früheren Java-Versionen unter 1.5 nicht unterstützt (die Verwendung des Schlüsselworts volatile ist seit der Version 1.5 behoben)
Klasseninhaber
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
Vorteile:
-
Verzögerte Initialisierung.
-
Thread-Sicherheit.
-
Hohe Leistung in einer Multithread-Umgebung.
-
Für den korrekten Betrieb ist eine Garantie erforderlich, dass das Singleton- Objekt fehlerfrei initialisiert wird. Andernfalls führt der erste Aufruf der Methode getInstance zu einem ExceptionInInitializerError und alle nachfolgenden Aufrufe führen zu einem NoClassDefFoundError .
Implementierung | Verzögerte Initialisierung | Thread-Sicherheit | Multithread-Leistung | Wann verwenden? |
---|---|---|---|---|
Einfach | - | + | Schnell | Niemals. Oder möglicherweise, wenn eine verzögerte Initialisierung nicht wichtig ist. Aber nie wäre es besser. |
Verzögerte Initialisierung | + | - | Unzutreffend | Immer wenn Multithreading nicht benötigt wird |
Synchronisierter Zugriff | + | + | Langsam | Niemals. Oder möglicherweise, wenn die Multithread-Leistung keine Rolle spielt. Aber nie wäre es besser. |
Doppelt überprüfte Verriegelung | + | + | Schnell | In seltenen Fällen, wenn Sie beim Erstellen des Singletons Ausnahmen behandeln müssen (wenn der Klassenhalter-Singleton nicht anwendbar ist) |
Klasseninhaber | + | + | Schnell | Immer wenn Multithreading erforderlich ist und eine Garantie dafür besteht, dass das Singleton-Objekt problemlos erstellt wird. |
Vor- und Nachteile des Singleton-Musters
Im Allgemeinen macht ein Singleton genau das, was von ihm erwartet wird:-
Es garantiert, dass es immer nur eine Instanz der Klasse gibt.
-
Es bietet einen einzigen globalen Zugriffspunkt auf diese Instanz.
-
Ein Singleton verstößt gegen das Single-Responsibility-Prinzip: Zusätzlich zu seinen direkten Aufgaben kontrolliert die Singleton-Klasse auch die Anzahl der Instanzen.
-
Die Abhängigkeit einer gewöhnlichen Klasse von einem Singleton ist im öffentlichen Vertrag der Klasse nicht sichtbar.
-
Globale Variablen sind schlecht. Letztendlich wird aus einem Singleton eine umfangreiche globale Variable.
-
Das Vorhandensein eines Singletons verringert die Testbarkeit der Anwendung als Ganzes und insbesondere der Klassen, die den Singleton verwenden.