Maison > Java > javaDidacticiel > le corps du texte

Mode singleton thread-safe du multi-threading Java

高洛峰
Libérer: 2017-01-05 16:48:23
original
1345 Les gens l'ont consulté

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;
  }
}
Copier après la connexion

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());
  }
}
Copier après la connexion

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();
  }
}
Copier après la connexion

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;
  }
}
Copier après la connexion
.

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());
  }
}
Copier après la connexion

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();
  }
}
Copier après la connexion

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();
  }
}
Copier après la connexion

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 : <. 🎜>

La première solution, la plus courante, est d'ajouter synchronisé , et synchronisé peut être ajouté à différentes positions

La première, le verrouillage de méthode

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;
  }
}
Copier après la connexion
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;
  }
}
Copier après la connexion
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;
  }
}
Copier après la connexion
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 ?

Bien que l'instruction de création d'objet soit verrouillée, un seul thread peut terminer la création à la fois. Cependant, lorsque le premier thread arrive pour créer l'objet Object, le deuxième thread peut toujours continuer à le créer. nous verrouillons uniquement l'instruction create, la solution à ce problème

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;
  }
}
Copier après la connexion
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

Code principal

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;
  }
}
Copier après la connexion
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());
  }
}
Copier après la connexion
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();
  }
}
Copier après la connexion
Résultat

1851889404

1851889404
1851889404
1851889404
1851889404
Le mode singleton thread-safe est obtenu grâce à des classes statiques internes

4 Mode singleton de sérialisation et de désérialisation

Les classes statiques intégrées peuvent atteindre les threads. C'est une sécurité. problème, mais si vous rencontrez un objet sérialisé, le résultat obtenu en utilisant la méthode par défaut est toujours plusieurs instances

Code MyObject

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;
//  }
}
Copier après la connexion
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();
    }
     
     
  }
}
Copier après la connexion
Résultat

970928725

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;
 }
Copier après la connexion
lors de la désérialisation Lorsqu'il est appelé, vous pouvez. obtenir le même objet

System.out.println(myObject.readResolve().hashCode());

Result

1255301379

La méthode readResolve est appelée !
1255301379
Le même hashCode prouve que le même objet est obtenu

5. Utilisez un bloc de code statique pour implémenter singleton

Le code dans le bloc de code statique sera modifié lors de l'utilisation la classe Il a été exécuté, vous pouvez donc utiliser la fonctionnalité de code statique pour implémenter le mode à intérêt unique

Classe MyObject

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;
  }
}
Copier après la connexion
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());
    }
  }
}
Copier après la connexion
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();
  }
}
Copier après la connexion
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;
  }
}
Copier après la connexion

线程类

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());
    }
  }
}
Copier après la connexion

运行类

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();
  }
}
Copier après la connexion

运行结果

调用了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();
  }
}
Copier après la connexion

更改线程代码

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());
    }
  }
}
Copier après la connexion

结果
调用了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中文网!


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal