Maison >Java >javaDidacticiel >Mode singleton thread-safe du multi-threading Java
Concept :
Le modèle singleton est un modèle de conception courant en Java. Il existe trois types de modèles singleton : le singleton de style paresseux, le singleton de style affamé et le singleton de style d'enregistrement.
Le modèle singleton a les caractéristiques suivantes :
1. La classe singleton ne peut avoir qu'une seule instance.
2. La classe singleton doit créer sa propre instance unique.
3. La classe singleton doit fournir cette instance à tous les autres objets.
Le modèle singleton garantit qu'une classe n'a qu'une seule instance, s'instancie et fournit cette instance à l'ensemble du système. Dans les systèmes informatiques, les pools de threads, les caches, les objets de journal, les boîtes de dialogue, les imprimantes et les objets pilotes de carte graphique sont souvent conçus comme des singletons. Ces applications disposent toutes plus ou moins des fonctionnalités de gestionnaires de ressources. Chaque ordinateur peut avoir plusieurs imprimantes, mais il ne peut y avoir qu'un seul spouleur d'imprimante pour empêcher que deux travaux d'impression ne soient envoyés simultanément sur l'imprimante. Chaque ordinateur peut avoir plusieurs ports de communication, et le système doit gérer ces ports de communication de manière centralisée pour empêcher qu'un port de communication soit appelé par deux requêtes en même temps. En bref, le but du choix du mode singleton est d'éviter les états incohérents et d'éviter les politiques à long terme.
Ici, nous présentons principalement deux types en détail : le style chinois paresseux et le style chinois affamé
1 Chargement immédiat/style chinois affamé
L'instance a été créée avant d'appeler le. 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; } }
Créer une classe de thread
package com.weishiyao.learn.day8.singleton.ep1; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }
Créer une classe en cours d'exécution
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(); } }
Exécuter le résultat
167772895
167772895
167772895
Le hashCode a la même valeur, indiquant que l'objet est également le même, indiquant que le mode de chargement immédiat à intérêt unique a été implémenté
2. Chargement paresseux/style paresseux
L'instance n'apparaîtra pas tant que la méthode appelée n'est pas créée. Le plan d'implémentation peut consister à placer l'instanciation dans le constructeur sans paramètre, de sorte qu'une instance de l'objet ne soit créée que lorsqu'elle est appelée Code :
package 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; } }.
Créer une classe de thread
package com.weishiyao.learn.day8.singleton.ep2; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }
Créer une classe en cours d'exécution
package com.weishiyao.learn.day8.singleton.ep2; public class Run { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); } }
Résultat en cours d'exécution
167772895
Bien qu'une instance d'un objet soit retiré, s'il est dans un environnement multithread, il y aura plusieurs instances, donc ce n'est plus un mode singleton
Exécuter la classe de test
package 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(); } }
Résultat d'exécution
980258163
1224717057
1851889404
188820504
1672864109
Puisqu'il y a un problème, il faut résoudre le problème La solution multithread en mode paresseux, code : <.>
package 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; } }Cette synchronisation synchronisée Le schéma entraîne une efficacité trop faible et toute la méthode est verrouillée
Le deuxième plan d'utilisation synchronisé
package 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; } }Cette méthode est également très inefficace. Tous les codes de la méthode sont verrouillés. il suffit de verrouiller le code clé. La troisième utilisation synchronisée La solution
package 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; } }semble être la solution optimale, mais après avoir exécuté le résultats, j'ai trouvé qu'il n'est en fait pas thread-safe Résultat : 1224717057
971173439
1851889404
1224717057
1672864109
Pourquoi ?
package 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; } }est simplement d'ajouter un autre jugement dans le verrou pour garantir un singleton. Il s'agit du mécanisme de double vérification DCL
Les résultats. sont les suivants :1224717057
1224717057
1224717057
1224717057
1224717057
3. Utilisez des classes statiques intégrées pour implémenter des singletons
package 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; } }Code de classe de thread
package com.weishiyao.learn.day8.singleton.ep4; public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }Classe d'exécution
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(); } }Résultat1851889404
1851889404
1851889404
1851889404
1851889404
Le mode singleton thread-safe est obtenu grâce à des classes statiques internes
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; // } }Classe affaires
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(); } } }Résultat970928725
1099149023
Deux hashCode différents, prouvant qu'il ne s'agit pas du même objet Solution, ajoutez le code suivant
protected MyObject readResolve() { System.out.println("调用了readResolve方法!"); return MyObjectHandler.myObject; }lors de la désérialisation Lorsqu'il est appelé, vous pouvez. obtenir le même objet System.out.println(myObject.readResolve().hashCode());
Result
La méthode readResolve est appelée !
1255301379
Le même hashCode prouve que le même objet est obtenu
package com.weishiyao.learn.day8.singleton.ep6; public class MyObject { private static MyObject instance = null; private MyObject() { super(); } static { instance = new MyObject(); } public static MyObject getInstance() { return instance; } }Classe Thread
package com.weishiyao.learn.day8.singleton.ep6; public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(MyObject.getInstance().hashCode()); } } }Exécuter la classe
package com.weishiyao.learn.day8.singleton.ep6; 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(); } }Résultat de l'exécution :
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中文网!