Heim >Java >javaLernprogramm >Thread-sicherer Singleton-Modus von Java-Multithreading
Konzept:
Das Singleton-Muster ist ein gängiges Entwurfsmuster in Java. Es gibt drei Arten von Singleton-Mustern: Singleton im Lazy-Stil, Singleton im Hungrig-Stil und Singleton im Registrierungsstil.
Das Singleton-Modell weist die folgenden Merkmale auf:
1. Die Singleton-Klasse kann nur eine Instanz haben.
2. Die Singleton-Klasse muss ihre eigene eindeutige Instanz erstellen.
3. Die Singleton-Klasse muss diese Instanz allen anderen Objekten bereitstellen.
Das Singleton-Muster stellt sicher, dass eine Klasse nur eine Instanz hat, sich selbst instanziiert und diese Instanz dem gesamten System zur Verfügung stellt. In Computersystemen werden Thread-Pools, Caches, Protokollobjekte, Dialogfelder, Drucker und Grafikkartentreiberobjekte häufig als Singletons konzipiert. Diese Anwendungen verfügen alle mehr oder weniger über die Funktionalität von Ressourcenmanagern. Jeder Computer kann über mehrere Drucker verfügen, es kann jedoch nur ein Druckerspooler vorhanden sein, um zu verhindern, dass zwei Druckaufträge gleichzeitig auf dem Drucker ausgegeben werden. Jeder Computer kann über mehrere Kommunikationsanschlüsse verfügen. Das System sollte diese Kommunikationsanschlüsse zentral verwalten, um zu verhindern, dass ein Kommunikationsanschluss gleichzeitig von zwei Anforderungen aufgerufen wird. Kurz gesagt besteht der Zweck der Wahl des Singleton-Modus darin, inkonsistente Zustände und langfristige Richtlinien zu vermeiden.
Hier stellen wir hauptsächlich zwei Typen im Detail vor: Lazy Chinese Style und Hungry Chinese Style
1. Sofortiges Laden/Hungry Chinese Style
Die Instanz wurde vor dem Aufruf erstellt Methode. Code:
package com.weishiyao.learn.day8.singleton.ep1; public class MyObject { // 立即加载方式==恶汉模式 private static MyObject myObject = new MyObject(); private MyObject() { } public static MyObject getInstance() { // 此代码版本为立即加载 // 此版本代码的缺点是不能有其他实例变量 // 因为getInstance()方法没有同步 // 所以有可能出现非线程安全的问题 return myObject; } }
Thread-Klasse erstellen
package com.weishiyao.learn.day8.singleton.ep1; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }
Laufende Klasse erstellen
package com.weishiyao.learn.day8.singleton.ep1; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } }
Ergebnis ausführen
167772895
167772895
167772895
Der HashCode ist derselbe Wert, was darauf hinweist, dass das Objekt ebenfalls dasselbe ist, was darauf hinweist, dass der Single-Interest-Modus für sofortiges Laden implementiert wurde
2. Lazy Loading/Lazy Style
Die Instanz wird erst angezeigt, wenn die Methode aufgerufen wird. Die Implementierungslösung kann darin bestehen, die Instanziierung in den parameterlosen Konstruktor einzufügen, sodass eine Instanz des Objekts nur erstellt wird, wenn sie aufgerufen wird
Thread-Klasse erstellenpackage com.weishiyao.learn.day8.singleton.ep2; public class MyObject { private static MyObject myObject; private MyObject() { } public static MyObject getInstance() { // 延迟加载 if (myObject != null) { } else { myObject = new MyObject(); } return myObject; } }Eine laufende Klasse erstellen
package com.weishiyao.learn.day8.singleton.ep2; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }Laufendes Ergebnis
package com.weishiyao.learn.day8.singleton.ep2; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); } }
167772895
Obwohl eine Instanz eines Objekts ist herausgenommen, wenn es sich in einer Multithread-Umgebung befindet, Es wird mehrere Instanzen geben, es handelt sich also nicht mehr um einen Singleton-Modus
Führen Sie die Testklasse aus
Laufergebnispackage com.weishiyao.learn.day8.singleton.ep2; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); MyThread t4 = new MyThread(); MyThread t5 = new MyThread(); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
980258163
12247170571851889404
188820504
1672864109
Da ein Problem vorliegt, muss das Problem im Lazy-Modus gelöst werden, Code:
Die erste Lösung, die am häufigsten vorkommt, besteht darin, synchronisiert hinzuzufügen. Synchronisiert kann an verschiedenen Positionen hinzugefügt werden
Die erste Lösung ist die Methodensperre
Diese synchronisierte Synchronisierung Das Schema führt zu einer zu geringen Effizienz und die gesamte Methode ist gesperrtpackage com.weishiyao.learn.day8.singleton.ep3; public class MyObject { private static MyObject myObject; private MyObject() { } synchronized public static MyObject getInstance() { // 延迟加载 try { if (myObject != null) { } else { // 模拟在创建对象之前做一些准备性的工作 Thread.sleep(2000); myObject = new MyObject(); } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
Der zweite synchronisierte Nutzungsplan
Diese Methode ist ebenfalls sehr ineffizient Sie müssen nur den Schlüsselcode sperren. Die Lösungpackage com.weishiyao.learn.day8.singleton.ep3; public class MyObject { private static MyObject myObject; private MyObject() { } public static MyObject getInstance() { // 延迟加载 try { synchronized (MyObject.class) { if (myObject != null) { } else { // 模拟在创建对象之前做一些准备性的工作 Thread.sleep(2000); myObject = new MyObject(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }Paket com.weishiyao.learn.day8.singleton.ep3;
public class MyObject { private static MyObject myObject; private MyObject() { } public static MyObject getInstance() { // 延迟加载 try { if (myObject != null) { } else { // 模拟在创建对象之前做一些准备性的工作 Thread.sleep(2000); synchronized (MyObject.class) { myObject = new MyObject(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
Obwohl die Objekterstellungsanweisung gesperrt ist, kann jeweils nur ein Thread die Erstellung abschließen. Wenn jedoch der erste Thread eingeht, um das Objektobjekt zu erstellen, kann der zweite Thread weiterhin mit der Erstellung fortfahren Wir sperren nur die create-Anweisung. Die Lösung für dieses Problem
besteht lediglich darin, der Sperre ein weiteres Urteil hinzuzufügen, um einen Singleton sicherzustellen. Dies ist der DCL-Doppelprüfmechanismus
Die Ergebnisse sind wie folgt:
1224717057
1224717057
1224717057
1224717057
3. Verwenden Sie integrierte statische Klassen, um Singletons zu implementierenpackage com.weishiyao.learn.day8.singleton.ep3; public class MyObject { private static MyObject myObject; private MyObject() { } public static MyObject getInstance() { // 延迟加载 try { if (myObject != null) { } else { // 模拟在创建对象之前做一些准备性的工作 Thread.sleep(2000); synchronized (MyObject.class) { if (myObject == null) { myObject = new MyObject(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
Main.code
Thread-Klassencode
Klasse ausführen
Ergebnis
1851889404
1851889404package com.weishiyao.learn.day8.singleton.ep4; public class MyObject { // 内部类方式 private static class MyObjectHandler { private static MyObject myObject = new MyObject(); } public MyObject() { } public static MyObject getInstance() { return MyObjectHandler.myObject; } }1851889404
1851889404
1851889404package com.weishiyao.learn.day8.singleton.ep4; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }Der Thread-sichere Singleton-Modus wird durch interne statische Klassen erreicht4. Serialisierungs- und Deserialisierungs-Singleton-Modus
package com.weishiyao.learn.day8.singleton.ep4; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); MyThread t4 = new MyThread(); MyThread t5 = new MyThread(); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
Eingebaute statische Klassen können Threads erreichen Es ist eine Sicherheit Problem, aber wenn Sie auf ein serialisiertes Objekt stoßen, ist das mit der Standardmethode erhaltene Ergebnis immer noch mehrere Instanzen
MyObject-Code
Business-Klasse
1099149023
Zwei verschiedene Hash-Codes, um zu beweisen, dass es sich nicht um dasselbe Objekt handelt. Fügen Sie während der Deserialisierung den folgenden Code hinzu Holen Sie sich das gleiche Objekt System.out.println(myObject.readResolve().hashCode());package com.weishiyao.learn.day8.singleton.ep5; import java.io.Serializable; public class MyObject implements Serializable { /** * */ private static final long serialVersionUID = 888L; // 内部类方式 private static class MyObjectHandler { private static MyObject myObject = new MyObject(); } public MyObject() { } public static MyObject getInstance() { return MyObjectHandler.myObject; } // protected MyObject readResolve() { // System.out.println("调用了readResolve方法!"); // return MyObjectHandler.myObject; // } }Ergebnis 1255301379
package com.weishiyao.learn.day8.singleton.ep5; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SaveAndRead { public static void main(String[] args) { try { MyObject myObject = MyObject.getInstance(); FileOutputStream fosRef = new FileOutputStream(new File("myObjectFile.txt")); ObjectOutputStream oosRef = new ObjectOutputStream(fosRef); oosRef.writeObject(myObject); oosRef.close(); fosRef.close(); System.out.println(myObject.hashCode()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } FileInputStream fisRef; try { fisRef = new FileInputStream(new File("myObjectFile.txt")); ObjectInputStream iosRef = new ObjectInputStream(fisRef); MyObject myObject = (MyObject) iosRef.readObject(); iosRef.close(); fisRef.close(); System.out.println(myObject.hashCode()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }Die readResolve-Methode wird aufgerufen!
1255301379
Derselbe HashCode beweist, dass dasselbe Objekt erhalten wird
5. Verwenden Sie einen statischen Codeblock, um Singleton zu implementieren
protected MyObject readResolve() { System.out.println("调用了readResolve方法!"); return MyObjectHandler.myObject; }
MyObject-Klasse
Thread-Klasse
Klasse ausführen
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
通过静态代码块只执行一次的特性也成功的得到了线程安全的单例模式
六、使用enum枚举数据类型实现单例模式
枚举enum和静态代码块的特性类似,在使用枚举时,构造方法会被自动调用,也可以用来实现单例模式
MyObject类
package com.weishiyao.learn.day8.singleton.ep7; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public enum MyObject { connectionFactory; private Connection connection; private MyObject() { try { System.out.println("调用了MyObject的构造"); String url = "jdbc:mysql://172.16.221.19:3306/wechat_1?useUnicode=true&characterEncoding=UTF-8"; String name = "root"; String password = "111111"; String driverName = "com.mysql.jdbc.Driver"; Class.forName(driverName); connection = DriverManager.getConnection(url, name, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public Connection getConnection() { return connection; } }
线程类
package com.weishiyao.learn.day8.singleton.ep7; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(MyObject.connectionFactory.getConnection().hashCode()); } } }
运行类
package com.weishiyao.learn.day8.singleton.ep7; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); MyThread t4 = new MyThread(); MyThread t5 = new MyThread(); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
运行结果
调用了MyObject的构造
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
上面这种写法将枚举类暴露了,违反了“职责单一原则”,可以使用一个类将枚举包裹起来
package com.weishiyao.learn.day8.singleton.ep8; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MyObject { public enum MyEnumSingleton { connectionFactory; private Connection connection; private MyEnumSingleton() { try { System.out.println("调用了MyObject的构造"); String url = "jdbc:mysql://172.16.221.19:3306/wechat_1?useUnicode=true&characterEncoding=UTF-8"; String name = "root"; String password = "111111"; String driverName = "com.mysql.jdbc.Driver"; Class.forName(driverName); connection = DriverManager.getConnection(url, name, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public Connection getConnection() { return connection; } } public static Connection getConnection() { return MyEnumSingleton.connectionFactory.getConnection(); } }
更改线程代码
package com.weishiyao.learn.day8.singleton.ep8; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(MyObject.getConnection().hashCode()); } } }
结果
调用了MyObject的构造
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
以上总结了单利模式与多线程结合时遇到的各种情况和解决方案,以供以后使用时查阅。
更多java多线程之线程安全的单例模式相关文章请关注PHP中文网!