Heim  >  Artikel  >  Java  >  Lass deinen Singleton nicht kaputt gehen! Hier erfahren Sie, wie Sie es in Java zu % Thread-sicher machen

Lass deinen Singleton nicht kaputt gehen! Hier erfahren Sie, wie Sie es in Java zu % Thread-sicher machen

Patricia Arquette
Patricia ArquetteOriginal
2024-11-02 09:55:02277Durchsuche

Don’t Let Your Singleton Break! Here’s How to Make It % Thread-Safe in Java

In diesem Beitrag untersuchen wir verschiedene Möglichkeiten zur Implementierung eines Thread-sicheren Singletons in Java, einschließlich eifriger Initialisierung und doppelt überprüfter Sperrung und der innere statische Klasse-Ansatz. Wir werden auch diskutieren, warum das letzte Schlüsselwort für die Gewährleistung der Integrität eines Singletons von Vorteil ist.

Warum einen Singleton verwenden?

Singletons sind nützlich, wenn Sie in der gesamten Anwendung genau eine Instanz einer Klasse benötigen. Zu den häufigsten Anwendungsfällen gehört die Verwaltung gemeinsam genutzter Ressourcen wie Protokollierung, Konfiguration oder Verbindungspools. Ein Singleton stellt sicher, dass mehrere Anfragen zum Zugriff auf eine Klasse dieselbe Instanz nutzen, anstatt neue zu erstellen.

1. Eager Initialization: Der einfachste Singleton

Das Eager-Initialisierungsmuster erstellt die Singleton-Instanz, wenn die Klasse geladen wird. Dies ist unkompliziert und gewährleistet Thread-Sicherheit, da die Instanz erstellt wird, wenn die Klasse von der JVM geladen wird.

public final class Singleton {
    // Instance is created at class loading time
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Vor- und Nachteile einer Eager-Initialisierung

Vorteile:

  • Standardmäßig einfach und threadsicher, da die JVM garantiert, dass die Klasseninitialisierung threadsicher ist.
  • Keine Synchronisierung oder zusätzliche Komplexität erforderlich.

Nachteile:

  • Die Instanz wird erstellt, unabhängig davon, ob sie verwendet wird oder nicht, was zu einer Verschwendung von Ressourcen führen kann, wenn der Singleton nie benötigt wird.

Wann man es verwendet: Eine eifrige Initialisierung ist am besten, wenn die Singleton-Klasse leichtgewichtig ist und Sie sicher sind, dass sie während der Laufzeit der Anwendung verwendet wird.


2. Lazy Initialization mit Double-Checked Locking

In Fällen, in denen Sie die Erstellung des Singletons verzögern möchten, bis er benötigt wird (bekannt als verzögerte Initialisierung), bietet doppelt überprüftes Sperren eine Thread-sichere Lösung. Es verwendet eine minimale Synchronisierung und stellt sicher, dass die Instanz nur erstellt wird, wenn zum ersten Mal darauf zugegriffen wird.

public final class Singleton {  // Marked as final to prevent subclassing

    // volatile ensures visibility and prevents instruction reordering
    private static volatile Singleton instance;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {               // First check (no locking)
            synchronized (Singleton.class) {   // Locking
                if (instance == null) {        // Second check (with locking)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Warum die doppelt überprüfte Verriegelung funktioniert

  1. Erste Prüfung: Die if (instance == null)-Prüfung außerhalb des synchronisierten Blocks ermöglicht es uns, eine Sperrung bei jedem Aufruf von getInstance() zu vermeiden. Dies verbessert die Leistung, indem der synchronisierte Block für zukünftige Aufrufe nach der Initialisierung umgangen wird.

  2. Synchronisierter Block: Sobald die Instanz null ist, stellt die Eingabe des synchronisierten Blocks sicher, dass nur ein Thread die Singleton-Instanz erstellt. Andere Threads, die diesen Punkt erreichen, müssen warten, um Race Conditions zu vermeiden.

  3. Zweite Prüfung: Innerhalb des synchronisierten Blocks überprüfen wir die Instanz erneut, um sicherzustellen, dass kein anderer Thread sie initialisiert hat, während der aktuelle Thread wartete. Durch diese Doppelprüfung wird sichergestellt, dass nur eine Singleton-Instanz erstellt wird.

Warum Volatilität benötigt wird

Das Schlüsselwort volatile ist im doppelt überprüften Sperrmuster unerlässlich, um eine Neuordnung der Anweisungen zu verhindern. Ohne sie wäre die Anweisung „instance = new Singleton();“ kann für andere Threads als abgeschlossen erscheinen, bevor es vollständig initialisiert ist, was dazu führt, dass eine teilweise erstellte Instanz zurückgegeben wird. volatile garantiert, dass eine Instanz, sobald sie nicht null ist, vollständig aufgebaut und für alle Threads sichtbar ist.

Warum endgültig gute Praxis ist

Das letzte Schlüsselwort wird hier verwendet, um Unterklassen zu verhindern. Das Markieren der Singleton-Klasse als endgültig hat zwei Hauptvorteile:

  1. Verhindert Unterklassenbildung: Indem wir die Klasse endgültig machen, verhindern wir, dass andere Klassen sie erweitern. Dadurch wird sichergestellt, dass nur eine Instanz der Singleton-Klasse existieren kann, da die Unterklassenbildung zu zusätzlichen Instanzen führen könnte, die das Singleton-Muster zerstören würden.

  2. Signalisiert Unveränderlichkeit: final dient als klarer Indikator für andere Entwickler, dass die Singleton-Klasse unveränderlich sein soll und nicht erweitert werden sollte. Dadurch ist der Code leichter zu verstehen und zu warten.

Kurz gesagt, final stärkt die Integrität des Singletons und hilft, unerwartetes Verhalten durch Unterklassen zu vermeiden.

Vor- und Nachteile einer doppelt geprüften Verriegelung

Vorteile:

  • Eine verzögerte Initialisierung spart Ressourcen, indem die Erstellung verzögert wird, bis sie benötigt wird.
  • Minimaler Synchronisierungsaufwand durch doppelte Überprüfung.

Nachteile:

  • Etwas komplexer und erfordert aus Sicherheitsgründen flüchtige Bestandteile.
  • Kann für einfachere Singleton-Anforderungen, bei denen eine eifrige Initialisierung ausreicht, übertrieben sein.

Wann man es verwendet: Dieses Muster ist nützlich, wenn die Singleton-Klasse ressourcenintensiv ist und möglicherweise nicht immer benötigt wird, oder wenn die Leistung in einer Multithread-Umgebung ein Problem darstellt.


3. Innere statische Klasse: Eine sauberere Alternative zur verzögerten Initialisierung

Ein alternativer Thread-sicherer Ansatz zur verzögerten Initialisierung ist das Muster innere statische Klasse. Dies nutzt den Klassenlademechanismus von Java, um die Singleton-Instanz nur dann zu initialisieren, wenn sie benötigt wird, ohne explizite Synchronisierung.

public final class Singleton {
    // Instance is created at class loading time
    private static final Singleton INSTANCE = new Singleton();

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

So funktioniert die innere statische Klasse

In diesem Muster wird die SingletonHelper-Klasse nur geladen, wenn getInstance() zum ersten Mal aufgerufen wird. Dies löst die Initialisierung von INSTANCE aus und sorgt so für verzögertes Laden, ohne dass synchronisierte Blöcke erforderlich sind.

Vor- und Nachteile der inneren statischen Klasse

Vorteile:

  • Thread-sicher, ohne dass flüchtige oder synchronisierte Blöcke erforderlich sind.
  • Einfach und sauber, mit verzögerter Initialisierung durch Design.

Nachteile:

  • Etwas weniger intuitiv für neue Entwickler, die mit der Klassenlademechanik von Java nicht vertraut sind.

Wann man es verwendet: Verwenden Sie das innere statische Klassenmuster, wenn Sie eine verzögerte Initialisierung mit sauberem, wartbarem Code wünschen. Dies ist aufgrund der Einfachheit und Thread-Sicherheit oft die bevorzugte Wahl.


Zusammenfassung

Wir haben uns drei beliebte Möglichkeiten angesehen, einen Thread-sicheren Singleton in Java zu implementieren:

  1. Eager Initialization: Am besten für einfache Fälle geeignet, in denen der Singleton leichtgewichtig ist und immer verwendet wird.
  2. Doppelt überprüftes Sperren: Ideal für leistungsempfindliche Multithread-Umgebungen mit Bedarf an verzögerter Initialisierung.
  3. Innere statische Klasse: Ein sauberer und Thread-sicherer Ansatz zur verzögerten Initialisierung unter Verwendung des Klassenladeverhaltens von Java.

Jeder Ansatz hat seine Stärken und eignet sich für unterschiedliche Szenarien. Probieren Sie sie in Ihren eigenen Projekten aus und finden Sie heraus, welches für Sie am besten funktioniert! Lassen Sie mich in den Kommentaren wissen, wenn Sie einen bevorzugten Ansatz oder Fragen haben.

Viel Spaß beim Codieren! ?‍??‍?

Das obige ist der detaillierte Inhalt vonLass deinen Singleton nicht kaputt gehen! Hier erfahren Sie, wie Sie es in Java zu % Thread-sicher machen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn