Dahua-Designmuster – Singleton-Muster

Singleton

Absicht

Stellen Sie sicher, dass es nur eine Instanz einer Klasse gibt, und stellen Sie einen globalen Zugriffspunkt für diese Instanz bereit.

Klassen Diagramm

Implementiert mit einem privaten Konstruktor, einer privaten statischen Variablen und einer öffentlichen statischen Funktion.

Der private Konstruktor garantiert, dass die Objektinstanz nicht über den Konstruktor erstellt werden kann und nur die einzige private statische Variable über die öffentliche statische Funktion zurückgegeben werden kann.


Implementierung

Ⅰ Lazy Style-Thread unsicher

In der folgenden Implementierung wird die private statische Variable uniqueInstance verzögert instanziiert. Dies hat den Vorteil, dass uniqueInstance nicht instanziiert wird, wenn die Klasse nicht verwendet wird, wodurch Ressourcen gespart werden.

Diese Implementierung ist in einer Multithread-Umgebung unsicher. Wenn mehrere Threads gleichzeitig eintreten können if (uniqueInstance == null)und uniqueInstance zu diesem Zeitpunkt null ist, führen mehrere Threads uniqueInstance = new Singleton();die Anweisung aus, was zu mehreren Instanziierungen von uniqueInstance führt.

public class Singleton {
    
    

    private static Singleton uniqueInstance;

    private Singleton() {
    
    
    }

    public static Singleton getUniqueInstance() {
    
    
        if (uniqueInstance == null) {
    
    
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

Ⅱ Hungry Chinese Style-Thread Safety

Thread-Unsicherheit wird hauptsächlich dadurch verursacht, dass uniqueInstance mehrmals instanziiert wird. Die direkte Instanziierung von uniqueInstance führt nicht zu Thread-Unsicherheit.

Allerdings verliert die Methode der direkten Instanziierung auch den Vorteil der Ressourceneinsparung, die eine verzögerte Instanziierung mit sich bringt.

private static Singleton uniqueInstance = new Singleton();

Ⅲ Lazy-Style-Thread-Sicherheit

Nur die Methode getUniqueInstance() muss gesperrt werden, sodass jeweils nur ein Thread die Methode betreten kann, wodurch vermieden wird, dass uniqueInstance mehrmals instanziiert werden muss.

Wenn jedoch ein Thread diese Methode aufruft, müssen andere Threads, die versuchen, diese Methode aufzurufen, warten, auch wenn uniqueInstance instanziiert wurde. Dies führt dazu, dass der Thread zu lange blockiert, daher weist diese Methode Leistungsprobleme auf und wird nicht empfohlen.

public static synchronized Singleton getUniqueInstance() {
    
    
    if (uniqueInstance == null) {
    
    
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

Ⅳ Überprüfen Sie die Sicherheit des Sicherungsgewindes noch einmal

uniqueInstance muss nur einmal instanziiert werden und kann anschließend direkt verwendet werden. Der Sperrvorgang muss nur für den Teil des Codes durchgeführt werden, der instanziiert wird. Sperren ist nur erforderlich, wenn uniqueInstance nicht instanziiert wurde.

Die doppelte Prüfsperre beurteilt zunächst, ob die UniqueInstance instanziiert wurde, und sperrt dann die Instanziierungsanweisung, wenn sie nicht instanziiert wurde.

public class Singleton {
    
    

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    
    
    }

    public static Singleton getUniqueInstance() {
    
    
        if (uniqueInstance == null) {
    
    
            synchronized (Singleton.class) {
    
    
                if (uniqueInstance == null) {
    
    
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

Betrachten Sie die folgende Implementierung, die nur eine if-Anweisung verwendet. Wenn im Fall von uniqueInstance == null beide Threads die if-Anweisung ausführen, betreten beide Threads den if-Anweisungsblock. Obwohl es im if-Anweisungsblock eine Sperroperation gibt, führen beide Threads uniqueInstance = new Singleton();diese Anweisung aus. Da es sich nur um eine Frage der Reihenfolge handelt, wird sie zweimal instanziiert. Daher muss eine Doppelprüfungssperre verwendet werden, d. h. es müssen zwei if-Anweisungen verwendet werden: Die erste if-Anweisung wird verwendet, um den Sperrvorgang nach der Instanziierung von uniqueInstance zu vermeiden, und die zweite if-Anweisung ist gesperrt, also nur If ein Thread Wenn zwei Threads gleichzeitig Instanziierungsvorgänge ausführen, gibt es kein uniqueInstance == null.

if (uniqueInstance == null) {
    
    
    synchronized (Singleton.class) {
    
    
        uniqueInstance = new Singleton();
    }
}

Es ist auch erforderlich, UniqueInstance mit dem Schlüsselwort volatile zu ändern. uniqueInstance = new Singleton();Dieser Code wird tatsächlich in drei Schritten ausgeführt:

  1. Weisen Sie Speicherplatz für uniqueInstance zu
  2. UniqueInstance initialisieren
  3. Zeigen Sie uniqueInstance auf die zugewiesene Speicheradresse

Aufgrund der Befehlsneuanordnungsfunktion der JVM kann die Ausführungsreihenfolge jedoch zu 1>3>2 werden. Die Neuordnung von Anweisungen verursacht in einer Single-Thread-Umgebung keine Probleme, in einer Multi-Thread-Umgebung führt sie jedoch dazu, dass ein Thread eine Instanz erhält, die noch nicht initialisiert wurde. Beispielsweise führt Thread T 1 1 und 3 aus. Zu diesem Zeitpunkt ruft T 2 getUniqueInstance () auf und stellt fest, dass uniqueInstance nicht leer ist. Daher wird uniqueInstance zurückgegeben, aber uniqueInstance wurde zu diesem Zeitpunkt noch nicht initialisiert.

Durch die Verwendung von Volatilität kann die Befehlsneuanordnung der JVM verhindert werden, um sicherzustellen, dass sie auch in einer Multithread-Umgebung normal ausgeführt werden kann.

Ⅴ Statische Implementierung der inneren Klasse

Wenn die Singleton-Klasse geladen wird, wird die statische innere Klasse SingletonHolder nicht in den Speicher geladen. Der SingletonHolder wird nur geladen, wenn getUniqueInstance()die Methode aufgerufen wird , um die INSTANCE-Instanz auszulösen, und die JVM kann sicherstellen, dass die INSTANCE nur einmal instanziiert wird.SingletonHolder.INSTANCE

Dieser Ansatz hat nicht nur den Vorteil einer verzögerten Initialisierung, sondern bietet auch Thread-Sicherheitsunterstützung durch die JVM.

public class Singleton {
    
    

    private Singleton() {
    
    
    }

    private static class SingletonHolder {
    
    
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
    
    
        return SingletonHolder.INSTANCE;
    }
}

Ⅵ Aufzählungsimplementierung

public enum Singleton {
    
    

    INSTANCE;

    private String objName;


    public String getObjName() {
    
    
        return objName;
    }


    public void setObjName(String objName) {
    
    
        this.objName = objName;
    }


    public static void main(String[] args) {
    
    

        // 单例测试
        Singleton firstSingleton = Singleton.INSTANCE;
        firstSingleton.setObjName("firstName");
        System.out.println(firstSingleton.getObjName());
        Singleton secondSingleton = Singleton.INSTANCE;
        secondSingleton.setObjName("secondName");
        System.out.println(firstSingleton.getObjName());
        System.out.println(secondSingleton.getObjName());

        // 反射获取实例测试
        try {
    
    
            Singleton[] enumConstants = Singleton.class.getEnumConstants();
            for (Singleton enumConstant : enumConstants) {
    
    
                System.out.println(enumConstant.getObjName());
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}
firstName
secondName
secondName
secondName

Diese Implementierung schützt vor Reflection-Angriffen. In anderen Implementierungen kann die Zugriffsebene des privaten Konstruktors über die Methode setAccessible () auf öffentlich gesetzt werden, und dann wird der Konstruktor aufgerufen, um das Objekt zu instanziieren. Wenn Sie diesen Angriff verhindern möchten, müssen Sie eine Funktion hinzufügen Konstruktor, um mehrere Instanziierungen zu verhindern. Code. Die Implementierung garantiert durch die JVM, dass sie nur einmal instanziiert wird, sodass der oben erwähnte Reflexionsangriff nicht auftritt.

Diese Implementierung erhält nach mehrmaliger Serialisierung und Deserialisierung nicht mehrere Instanzen. Andere Implementierungen müssen transient verwenden, um alle Felder zu ändern und Serialisierungs- und Deserialisierungsmethoden zu implementieren.

Beispiele

  • Logger-Klassen
  • Konfigurationsklassen
  • Zugriff auf Ressourcen im freigegebenen Modus
  • Fabriken als Singletons implementiert

JDK

Je suppose que tu aimes

Origine blog.csdn.net/weixin_42917352/article/details/131227604
conseillé
Classement