Heim  >  Artikel  >  Java  >  Thread-sicherer Singleton-Modus von Java-Multithreading

Thread-sicherer Singleton-Modus von Java-Multithreading

高洛峰
高洛峰Original
2017-01-05 16:48:231310Durchsuche

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 erstellen
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;
  }
}

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

Laufergebnis
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();
  }
}

980258163

1224717057

1851889404
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 gesperrt
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;
  }
}

Der zweite synchronisierte Nutzungsplan

Diese Methode ist ebenfalls sehr ineffizient Sie müssen nur den Schlüsselcode sperren. Die Lösung
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;
  }
}
Paket com.weishiyao.learn.day8.singleton.ep3;


scheint die optimale Lösung zu sein, aber nach dem Ausführen des Ergebnisse: Ich habe festgestellt, dass es tatsächlich nicht threadsicher ist.
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

1224717057

3. Verwenden Sie integrierte statische Klassen, um Singletons zu implementieren
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;
  }
}

Main.code

Thread-Klassencode



Klasse ausführen


Ergebnis

1851889404

1851889404
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;
  }
}
1851889404

1851889404

1851889404
package 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 erreicht

4. 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

Ergebnis

970928725

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

Der Code im statischen Codeblock wird bei Verwendung geändert Die Klasse wurde ausgeführt, sodass Sie die statische Codefunktion verwenden können, um den Single-Interest-Modus zu implementieren
protected MyObject readResolve() {
   System.out.println("调用了readResolve方法!");
   return MyObjectHandler.myObject;
 }

MyObject-Klasse


Thread-Klasse


Klasse ausführen

Ergebnis 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中文网!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn