Singleton Design Pattern und Thread-Sicherheit in Java

Singleton ist eines der am häufigsten verwendeten Design Pattern, um die durch Anwendungen erstellten Objekte zu beschränken. Wenn Sie es in einer Multi-Thread-Umgebung verwenden, dann ist die Thread-Sicherheit der Singleton-Klasse sehr wichtig. In realen Anwendungen sind Ressourcen wie Datenbankverbindungen oder Unternehmensinformationssysteme (EIS) begrenzt und sollten klug eingesetzt werden, um Engpässe zu vermeiden. Um dies zu erreichen, können wir das Singleton Design Pattern implementieren. Wir können eine Wrapper-Klasse für die Ressource erstellen und die Anzahl der zur Laufzeit erstellten Objekte auf eins begrenzen.

Thread-sicheres Singleton in Java

Allgemein folgen wir den untenstehenden Schritten, um eine Singleton-Klasse zu erstellen:

  1. Erstellen Sie den privaten Konstruktor, um die Erstellung neuer Objekte mit dem new-Operator zu verhindern.
  2. Deklarieren Sie eine private statische Instanz derselben Klasse.
  3. Stellen Sie eine öffentliche statische Methode bereit, die die Singleton-Klasseninstanz zurückgibt. Wenn die Variable nicht initialisiert ist, dann initialisieren Sie sie, oder geben Sie einfach die Instanzvariable zurück.

Mit den obigen Schritten habe ich eine Singleton-Klasse erstellt, die wie folgt aussieht. ASingleton.java

package com.journaldev.designpatterns;

public class ASingleton {

	private static ASingleton instance = null;

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		if (instance == null) {
			instance = new ASingleton();
		}
		return instance;
	}

}


Im obigen Code ist die getInstance()-Methode nicht thread-sicher. Mehrere Threads können gleichzeitig darauf zugreifen. Für die ersten paar Threads, wenn die Instanzvariable noch nicht initialisiert ist, können mehrere Threads in die if-Schleife eintreten und mehrere Instanzen erstellen. Dies würde unsere Singleton-Implementierung brechen.

Wie erreicht man ein thread-sicheres Singleton in Java?

Es gibt drei Wege, um Thread-Sicherheit zu erreichen.

Erstellen Sie die Instanzvariable zum Zeitpunkt des Klassenladens.

Vorteile:

  • Thread-Sicherheit ohne Synchronisierung
  • Einfach zu implementieren

Nachteile:

  • Frühe Erstellung von Ressourcen, die möglicherweise nicht in der Anwendung verwendet werden.
  • Die Client-Anwendung kann keine Argumente übergeben, daher können wir sie nicht wiederverwenden. Zum Beispiel, eine generische Singleton-Klasse für Datenbankverbindungen, bei der die Client-Anwendung Datenbankservereigenschaften liefert.

Synchronisieren Sie die getInstance()-Methode.

Vorteile:

  • Thread-Sicherheit ist garantiert.
  • Client-Anwendung kann Parameter übergeben
  • Lazy Initialization erreicht

Nachteile:

  • Langsame Leistung wegen der Sperrung.
  • Unnötige Synchronisation, die nicht mehr erforderlich ist, sobald die Instanzvariable initialisiert ist.

Verwenden Sie einen synchronisierten Block innerhalb der if-Schleife und eine volatile Variable.

Vorteile:

  • Thread-Sicherheit ist garantiert
  • Client-Anwendung kann Argumente übergeben
  • Lazy Initialization erreicht
  • Die Synchronisationsbelastung ist minimal und gilt nur für die ersten paar Threads, wenn die Variable null ist.

Nachteile:

  • Zusätzliche if-Bedingung

Betrachtet man alle drei Möglichkeiten zur Erreichung der Thread-Sicherheit, denke ich, dass die dritte die beste Option ist. In diesem Fall würde die modifizierte Klasse so aussehen:

package com.journaldev.designpatterns;

public class ASingleton {

	private static volatile ASingleton instance;
	private static Object mutex = new Object();

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		ASingleton result = instance;
		if (result == null) {
			synchronized (mutex) {
				result = instance;
				if (result == null)
					instance = result = new ASingleton();
			}
		}
		return result;
	}

}


Die lokale Variable result scheint unnötig. Aber sie ist da, um die Leistung unseres Codes zu verbessern. In Fällen, in denen die Instanz bereits initialisiert ist (meistens), wird das volatile Feld nur einmal zugegriffen (aufgrund von „return result;“ statt „return instance;“). Dies kann die Gesamtleistung der Methode um bis zu 25 Prozent verbessern. Wenn Sie denken, dass es bessere Wege gibt, dies zu erreichen oder wenn die Thread-Sicherheit in der obigen Implementierung kompromittiert ist, bitte kommentieren und teilen Sie es mit uns allen.

Bonus-Tipp

String ist kein sehr guter Kandidat, um mit dem Schlüsselwort synchronized verwendet zu werden. Das liegt daran, dass sie in einem String-Pool gespeichert werden und wir keinen String sperren wollen, der möglicherweise von einem anderen Code verwendet wird. Deshalb verwende ich eine Object-Variable. Das ist alles, was Sie über das Singleton Design Pattern und Thread-Sicherheit in Java wissen müssen.

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren: