Dieser Artikel bietet Ihnen eine detaillierte Einführung in den Singleton-Modus (mit Code). Ich hoffe, er wird Ihnen als Referenz dienen.
单线程下的Singleton的稳定性是极好的,可分为两大类:
1. Eager (Hungry-Typ): Erstellen Sie sofort ein Objekt, wenn die Klasse geladen wird.
public class EagerSingleton { //1. 类加载时就立即产生实例对象,通过设置静态变量被外界获取 //2. 并使用private保证封装安全性 private static EagerSingleton eagerSingleton = new EagerSingleton(); //3. 通过构造方法的私有化,不允许外部直接创建对象,确保单例的安全性 private EagerSingleton(){ } public static EagerSingleton getEagerSingleton(){ return eagerSingleton; }
2. Lazy (Lazy-Typ): Das Objekt wird nicht sofort erstellt, wenn die Klasse geladen wird, und wird erst instanziiert, wenn der erste Benutzer es erhält.
public class LazySingleton { //1. 类加载时并没有创建唯一实例 private static LazySingleton lazySingleton; private LazySingleton() { } //2、提供一个获取实例的静态方法 public static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
In Bezug auf die Leistung ist LazySingleton offensichtlich besser als EagerSingleton. Wenn das Laden von Klassen viele Ressourcen erfordert (z. B. das Lesen großer Dateiinformationen), liegen die Vorteile von LazySingleton auf der Hand. Aber wenn man den Code liest, kann man leicht ein schwerwiegendes Problem erkennen. Wie kann die Sicherheit zwischen mehreren Threads aufrechterhalten werden?
Das Multi-Thread-Parallelitätsproblem wird im Folgenden analysiert:
Der Schlüssel zur Lösung dieses Problems liegt in zwei Aspekten: 1. Synchronisierung; 2. Leistung ;
Wie retten?
Als Java-Entwickler müssen Sie mit synchronisiert vertraut sein. Wenn es um Multithreading geht, denken die meisten Leute an ihn (nach JDK6 wurde seine Leistung erheblich verbessert und die Lösung). ist einfache Parallelität, sehr anwendbar).
Dann versuchen wir es mit der Synchronisierung zu lösen:
//由synchronized进行同步加锁 public synchronized static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
Das Synchronisationsproblem scheint also gelöst zu sein, aber als Entwickler ist es am wichtigsten, die Leistung sicherzustellen. Synchronisiert verwenden Es gibt Vor- und Nachteile. Aufgrund des Sperrvorgangs wird das Codesegment pessimistisch gesperrt. Erst wenn eine Anforderung abgeschlossen ist, kann die nächste Anforderung ausgeführt werden. Normalerweise sind Codeteile mit dem synchronisierten Schlüsselwort um ein Vielfaches langsamer als Code derselben Größenordnung, was wir nicht sehen möchten. Wie kann man dieses Problem vermeiden? In Javas Definition von synchronisiert gibt es einen solchen Vorschlag: Je später Sie synchronisiert verwenden, desto besser ist die Leistung (verfeinertes Sperren).
###### 2. Daher müssen wir mit der Lösung des Leistungsproblems beginnen. Optimieren Sie gemäß der Synchronisierung: ######
public class DoubleCheckLockSingleton { //使用volatile保证每次取值不是从缓存中取,而是从真正对应的内存地址中取.(下文解释) private static volatile DoubleCheckLockSingleton doubleCheckLockSingleton; private DoubleCheckLockSingleton(){ } public static DoubleCheckLockSingleton getDoubleCheckLockSingleton(){ //配置双重检查锁(下文解释) if(doubleCheckLockSingleton == null){ synchronized (DoubleCheckLockSingleton.class) { if(doubleCheckLockSingleton == null){ doubleCheckLockSingleton = new DoubleCheckLockSingleton(); } } } return doubleCheckLockSingleton; } }
Der obige Quellcode ist das klassische flüchtige Schlüsselwort (wiedergeboren nach JDK1.5) + Doppelprüfsperre (DoubleCheck) , maximale Optimierung Der durch die Synchronisierung verursachte Leistungsaufwand wird eliminiert. Volatile und DoubleCheck werden im Folgenden erläutert.
1.volatile
wurde nach JDK1.5 offiziell implementiert. Frühere Versionen definierten dieses Schlüsselwort nur ohne spezifische Implementierung. Wenn Sie Volatilität verstehen möchten, müssen Sie ein gewisses Verständnis für die Speicherverwaltung der JVM haben:
1.1 Nach dem Mooreschen Gesetz kann die Lese- und Schreibgeschwindigkeit des Speichers nicht mehr zufriedenstellend sein Daher haben moderne Computer einen Mechanismus zum Hinzufügen eines Caches zur CPU eingeführt. Der Cache liest den Speicherwert vor und speichert ihn vorübergehend im Cache. Durch Berechnung wird der entsprechende Wert im Speicher aktualisiert.
**1.2** Die JVM ahmt den Ansatz des PCs nach und teilt seinen eigenen **Arbeitsspeicher** in den Speicher auf. Dieser Teil des Speichers funktioniert genauso wie der Cache, was die Arbeit der JVM deutlich verbessert. Effizienz, aber alles hat Vor- und Nachteile. Dieser Ansatz führt auch zu Übertragungsproblemen, wenn das Arbeitsgedächtnis mit anderen Erinnerungen kommuniziert. Eine Funktion von volatile besteht darin, das Lesen des neuesten Werts aus dem Speicher zu erzwingen, um Inkonsistenzen zwischen Cache und Speicher zu vermeiden.
1.3 Eine weitere Funktion von Volatile hängt auch mit der JVM zusammen, das heißt, die JVM verwendet ihr eigenes Urteilsvermögen, um die Ausführungsreihenfolge des Quellcodes um die Kohärenz der Anweisungen in der Pipeline sicherzustellen, um den optimalen Ausführungsplan zu erreichen. Dieser Ansatz verbessert die Leistung, führt jedoch zu unerwarteten Ergebnissen für DoubleCheck und die beiden Threads können sich gegenseitig stören. Volatile bietet eine Garantie, bevor etwas passiert (Schreiben hat Vorrang vor Lesen), so dass das Objekt nicht gestört wird und Sicherheit und Stabilität gewährleistet sind.
2.DoubleCheck
Dies ist ein Erbe der modernen Programmierung. Es wird davon ausgegangen, dass das Objekt nach dem Eintritt in den synchronisierten Block instanziiert wurde und die Beurteilung erfolgen muss wieder gemacht werden. Natürlich gibt es eine andereoffiziell empfohlene Singleton-Implementierungsmethode:
Da die Konstruktion der Klasse bereits in der Definition atomar ist, sind alle oben genannten Probleme damit gelöst wird nicht erneut generiert. Dies ist eine gute Singleton-Implementierungsmethode und wird empfohlen.//使用内部类进行单例构造 public class NestedClassSingleton { private NestedClassSingleton(){ } private static class SingletonHolder{ private static final NestedClassSingleton nestedClassSingleton = new NestedClassSingleton(); } public static NestedClassSingleton getNestedClassSingleton(){ return SingletonHolder.nestedClassSingleton; } }
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in den Singleton-Modus in Parallelität (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!