• 周五. 12月 2nd, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Design pattern learning 04 (Java implementation) – singleton pattern

[db:作者]

1月 6, 2022

Write it at the front

  • Take notes on learning design patterns
  • Improve the flexible use of design patterns

Study address

https://www.bilibili.com/vide…

https://www.bilibili.com/vide…

Reference article

http://c.biancheng.net/view/1…

Project source code
https://gitee.com/zhuang-kang/DesignPattern

4, Creator mode

The main focus of creative patterns is “ How to create an object ?”, Its main feature is “ Separate the creation and use of objects ”.

This can reduce the coupling of the system , Users don’t need to pay attention to the details of object creation .

The creation mode is divided into two types :

  • The singleton pattern
  • Factory method model
  • Abstract engineering patterns
  • Archetypal model
  • Builder pattern

5, The singleton pattern

5.1 The definition and characteristics of singleton pattern

Single case (Singleton) Definition of pattern : A class has only one instance , And this class can create a pattern of this instance by itself . for example ,Windows Only one task manager can be opened in , This can avoid the waste of memory resources caused by opening multiple task manager windows , Or there are errors such as inconsistent display contents of each window .

In computer system , also Windows The recycle bin 、 File system in operating system 、 Thread pool in multithreading 、 Graphics card driver object 、 Background processing services for printers 、 The log object of the application 、 Connection pool for database 、 Counters for websites 、Web Applied configuration objects 、 Dialog boxes in the application 、 The cache in the system is often designed as a single example .

Singleton mode is also widely used in real life , For example, the company CEO、 Department managers, etc. are all singleton models .J2EE Standard ServletgContext and ServletContextConfig、Spring Framework application ApplicationContext、 The connection pool in the database is also singleton mode .

The singleton pattern is as follows 3 Characteristics :

  1. A singleton class has only one instance object ;
  2. The singleton object must be created by the singleton class itself ;
  3. The singleton class provides a global access point to access the singleton .

Advantages of singleton mode

  • The singleton mode ensures that there is only one instance in memory , Reduced memory overhead .
  • It can avoid multiple occupation of resources .
  • Singleton mode sets global access points , Can optimize and share access to resources .

Disadvantages of singleton mode :

  • Singleton mode generally has no interface , Extend the difficult . If you want to expand , In addition to modifying the original code , There is no second way , Against the principle of opening and closing .
  • In concurrent testing , Singleton mode is not good for code debugging . In the course of debugging , If the code in the singleton is not finished , You can’t simulate a new object .
  • The function code of singleton pattern is usually written in a class , If the function design is unreasonable , It is easy to violate the principle of single responsibility .

5.2 The structure and implementation of singleton pattern

5.2.1 The structure of singleton pattern

  1. Singleton class : A class that contains an instance and can create it by itself .
  2. Access class : Classes that use singletons .

5.2 Code implementation

There are two types of singleton design patterns :

 Hungry Chinese style : Class loading causes the singleton object to be created
Slacker type : Class loading does not cause the singleton object to be created , It's created when the object is first used 

Hungry Chinese style ( Static variables )

package com.zhuang.singleton.type1;
/**
* @Classname SingletonTest01
* @Description Hungry Chinese style ( Static variables )
* @Date 2021/3/17 9:26
* @Created by dell
*/
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
//1. Constructor privatization , External energy new、
private Singleton() {
}
// Create an object instance inside this class
private final static Singleton instance = new Singleton();
// Provide a public static method to the outside
public static Singleton getInstance() {
return instance;
}
}

<font color=’red’> explain :</font>

 This method declares... In the member position Singleton Static variables of type , And create Singleton Class object instance.instance Objects are created as classes load . If the object is large enough , And if you don't use it all the time, it will cause a waste of memory .

Static code block

package com.zhuang.singleton.type2;
/**
* @Classname SingletonTest02
* @Description Static code block
* @Date 2021/3/17 9:35
* @Created by dell
*/
public class SingletonTest02 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton2 {
//1. Constructor privatization , External energy new、
private Singleton2() {
}
// Create an object instance inside this class
private static Singleton2 instance;
/*
Creating objects in static code blocks
*/
static {
instance = new Singleton2();
}
// Provide a public static method to the outside
public static Singleton2 getInstance() {
return instance;
}
}

<font color=’red’> explain :</font>

 This method declares... In the member position Singleton Static variables of type , Objects are created in static blocks of code , Also created for loading classes . So with the starving way 1 Basically the same ,** Of course, this method also has the problem of memory waste .**

Slacker type Thread unsafe

package com.zhuang.singleton.type3;
/**
* @Classname SingletonTest03
* @Description Slacker type Thread unsafe
* @Date 2021/3/17 9:39
* @Created by dell
*/
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println(" Slacker type , Thread unsafe !!!");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
// Provides a static public method When this method is used , To create instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

<font color=’red’> explain :</font>

 From the above code, we can see that this way is declared in the member position Singleton Static variables of type , There is no object assignment operation , So when was the assignment made ? When calling getInstance() Method to get Singleton Class Singleton Class object , This achieves the effect of lazy loading . however , If it's a multithreaded environment , There will be thread safety issues .

Slacker type ( Thread safety , Synchronization method )

package com.zhuang.singleton.type4;
/**
* @Classname SingletonTest04
* @Description Slacker type ( Thread safety , Synchronization method )
* @Date 2021/3/17 9:46
* @Created by dell
*/
public class SingletonTest04 {
public static void main(String[] args) {
System.out.println(" Slacker type , Thread safety !!!");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
// Provides a static public method , Add the synchronization code , Solving thread safety problems
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

<font color=’red’> explain :</font>

 This method also realizes the lazy loading effect , At the same time, it solves the problem of thread safety . But in getInstance() Method added synchronized keyword , As a result, the execution effect of this method is very low . We can see from the above code that , It's initialization instance Thread safety issues only occur when the thread is safe , Once the initialization is complete, it doesn't exist .

Slacker type ( Thread safety , Synchronization code block )

package com.zhuang.singleton.type5;
/**
* @Classname SingletonTest05
* @Description Slacker type ( Thread safety , Synchronization code block )
* @Date 2021/3/17 9:50
* @Created by dell
*/
public class SingletonTest05 {
public static void main(String[] args) {
System.out.println(" Slacker type , Thread safety !, Synchronization code block ");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
// Provides a static public method , Add the synchronization code , Solving thread safety problems
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}

Double check lock mode is a very good single instance implementation mode , Solved the single case 、 performance 、 Thread safety problem , The double detection lock mode above looks perfect , In fact, there are problems , In the case of multithreading , There may be a null pointer problem , The reason for the problem is JVM When instantiating an object, it will perform optimization and instruction reordering operations .

To solve the problem of null pointer exception caused by double check lock mode , Just use volatile keyword , volatile Keywords ensure visibility and order .

package com.zhuang.singleton.type6;
/**
* @Classname SingletonTest06
* @Description Double check , Recommended
* @Date 2021/3/17 9:54
* @Created by dell
*/
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println(" Slacker type , Double check , Recommended ");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
// Provides a static public method , Add double check code , Add the synchronization code , Solve the problem of lazy loading
// Ensure efficiency . Recommended
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}

<font color=”red”> Summary :</font>

add to volatile The double check lock mode after the keyword is a better single instance implementation mode , It can ensure thread safety without performance problems in the case of multithreading .

Static inner class implements singleton pattern !

package com.zhuang.singleton.type7;
/**
* @Classname SingletonTest07
* @Description Static inner class implements singleton pattern !
* @Date 2021/3/17 9:59
* @Created by dell
*/
public class SingletonTest07 {
public static void main(String[] args) {
System.out.println(" Static inner class implements singleton pattern ");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
// Write a static inner class , There is a static property in this class ,Singleton
public static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
// Provides a static public method , Go straight back to SingletonInstance.INSTANCE;
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}

<font color=’red’> explain :</font>

 First load Singleton Class does not initialize INSTANCE, Only the first call getInstance, Virtual machine loading SingletonHolder

And initialization INSTANCE, This not only ensures thread safety , Also can guarantee Singleton Class uniqueness .

<font color=”red”> Summary :</font>

 Static inner class singleton mode is an excellent singleton mode , It is a single instance mode commonly used in open source projects . Without any locks , It ensures the security of multithreading , And there's no performance impact or space waste .

Enumeration to achieve singleton mode

package com.zhuang.singleton.type8;
/**
* @Classname SingletonTest08
* @Description Enumeration to achieve singleton mode
* @Date 2021/3/17 10:06
* @Created by dell
*/
public class SingletonTest08 {
public static void main(String[] args) {
System.out.println(" Enumeration to achieve singleton mode , Recommended ");
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);
// Judge whether it is a singleton
System.out.println(instance == instance2);
System.out.println("intstance Hash value of " + instance.hashCode());
System.out.println("intstance2 Hash value of " + instance2.hashCode());
}
}
/*
enumeration
*/
enum Singleton {
INSTANCE;// attribute
public void method() {
System.out.println("method() Method is called ...");
}
}

explain :

 Enumeration belongs to the evil Chinese way .

5.3 Application scenario of singleton mode

  • Some classes that need to be created frequently , Using singletons can reduce the memory pressure of the system , Reduce GC.
  • When a class only needs to generate one object , Like a monitor in a class 、 Everyone’s ID number, etc .
  • Some classes occupy more resources when creating instances , Or instantiation takes a long time , And often use .
  • A class needs to be instantiated frequently , When the created objects are destroyed frequently , Such as multithreaded thread pool 、 Network connection pool, etc .
  • Objects that frequently access databases or files .
  • For some control hardware level operations , Or from the system point of view, it should be a single control logic operation , If there are multiple instances , Then the system will be completely out of order .
  • When objects need to be shared . Because singleton mode allows only one object to be created , Sharing this object saves memory , And speed up object access . Such as Web Configuration object in 、 Database connection pool, etc .

5.4 The problem is

Break singleton mode :

Make the singleton class defined above (Singleton) You can create multiple objects , Except enumeration . There are two ways , They are serialization and reflection .

  • Serialization deserialization

    Singleton class :

    public class Singleton implements Serializable {
    // Private constructor
    private Singleton() {}
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    // Provide a static method to get the object
    public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    }

Test class :

public class Test {
public static void main(String[] args) throws Exception {
// Write objects to the file
//writeObject2File();
// Reading objects from a file
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
// Determine whether two deserialized objects are the same object
System.out.println(s1 == s2);
}
private static Singleton readObjectFromFile() throws Exception {
// Create object input stream object
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\dell\\Desktop\\a.txt"));
// The first read Singleton object
Singleton instance = (Singleton) ois.readObject();
return instance;
}
public static void writeObject2File() throws Exception {
// obtain Singleton Class object
Singleton instance = Singleton.getInstance();
// Create an object output stream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\dell\\Desktop\\a.txt"));
// take instance Object is written to a file
oos.writeObject(instance);
}
}

The result of the above code is
false, Indicates that serialization and deserialization have broken the singleton design pattern .

  • Reflection

    Singleton class :

    public class Singleton {
    // Private constructor
    private Singleton() {}
    private static volatile Singleton instance;
    // Provide a static method to get the object
    public static Singleton getInstance() {
    if(instance != null) {
    return instance;
    }
    synchronized (Singleton.class) {
    if(instance != null) {
    return instance;
    }
    instance = new Singleton();
    return instance;
    }
    }
    }

Test class :

public class Test {
public static void main(String[] args) throws Exception {
// obtain Singleton Class bytecode object
Class clazz = Singleton.class;
// obtain Singleton Class's private nonparametric construction method object
Constructor constructor = clazz.getDeclaredConstructor();
// Cancel access check
constructor.setAccessible(true);
// establish Singleton Class object s1
Singleton s1 = (Singleton) constructor.newInstance();
// establish Singleton Class object s2
Singleton s2 = (Singleton) constructor.newInstance();
// Judge the two created by reflection Singleton Whether the object is the same object
System.out.println(s1 == s2);
}
}

The result of the above code is false, Indicates that serialization and deserialization have broken the singleton design pattern

<font color=”red”> Be careful :</font> Enumeration does not cause these two problems .

Problem solving

  • serialize 、 The solution to breaking singleton pattern in reverse sequence mode

    stay Singleton Class readResolve() Method , Called by reflection on deserialization , If this method is defined , Return the value of this method , If there is no definition , Return to the new new Out object .

    Singleton class :

    public class Singleton implements Serializable {
    // Private constructor
    private Singleton() {}
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    // Provide a static method to get the object
    public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    /**
    * The following is to solve the problem of serialization and deserialization cracking singleton mode
    */
    private Object readResolve() {
    return SingletonHolder.INSTANCE;
    }
    }

The source code parsing :

ObjectInputStream class

public final Object readObject() throws IOException, ClassNotFoundException{
...
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);// Focus on readObject0 Method
.....
}
private Object readObject0(boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));// Focus on readOrdinaryObject Method
...
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
//isInstantiable return true, perform desc.newInstance(), Create a new singleton class by reflection ,
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// stay Singleton Class readResolve After the method desc.hasReadResolveMethod() The result of method execution is true
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
// Call by reflection Singleton Class readResolve Method , Assign return value to rep Variable
// So many times ObjectInputStream Class readObject Method , And then we call what we define readResolve Method , So it returns the same object .
Object rep = desc.invokeReadResolve(obj);
...
}
return obj;
}
  • The solution to cracking singletons by reflection

    public class Singleton {
    // Private constructor
    private Singleton() {
    /*
    Reflection cracking singleton mode needs to add code
    */
    if(instance != null) {
    throw new RuntimeException();
    }
    }
    private static volatile Singleton instance;
    // Provide a static method to get the object
    public static Singleton getInstance() {
    if(instance != null) {
    return instance;
    }
    synchronized (Singleton.class) {
    if(instance != null) {
    return instance;
    }
    instance = new Singleton();
    return instance;
    }
    }
    }

<font color=”red”> explain :</font>

 This is a better way to understand . When a constructor is called by reflection to create , Direct throw anomaly . Do not run this operation .

5.5 JDK The source code parsing -Runtime class

Runtime Class is the singleton design pattern used .

  1. Check which singleton mode is used through the source code

    public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    /**
    * Returns the runtime object associated with the current Java application.
    * Most of the methods of class <code>Runtime</code> are instance
    * methods and must be invoked with respect to the current runtime object.
    *
    * @return the <code>Runtime</code> object associated with the current
    * Java application.
    */
    public static Runtime getRuntime() {
    return currentRuntime;
    }
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    ...
    }

As you can see from the source code above Runtime Class uses the evil Chinese style ( Static attribute ) Method to realize singleton mode .

  1. Use Runtime Methods in class

    public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
    // obtain Runtime Class object
    Runtime runtime = Runtime.getRuntime();
    // return Java Total memory in the virtual machine .
    System.out.println(runtime.totalMemory());
    // return Java The maximum amount of memory a virtual machine tries to use .
    System.out.println(runtime.maxMemory());
    // Create a new process to execute the specified string command , Returns the process object
    Process process = runtime.exec("ipconfig");
    // Get the result of command execution , Get... Through the input stream
    InputStream inputStream = process.getInputStream();
    byte[] arr = new byte[1024 * 1024* 100];
    int b = inputStream.read(arr);
    System.out.println(new String(arr,0,b,"gbk"));
    }
    }

At the end

  • If my article is useful to you , Please order me a , Thank you for your !
  • There is a problem , Welcome to the comment area !

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注