"Design Pattern" Singleton Pattern

In some systems, in order to save memory resources and ensure the consistency of data content, only one instance of certain classes is required to be created. This is the so-called singleton mode.

1. Definition and characteristics of singleton pattern

The definition of singleton () pattern: refers to a pattern in which a class has only one instance, and the class can create this instance by itself. For example, only one Task Manager can be opened in Windows. This can avoid the waste of memory resources caused by opening multiple Task Manager windows, or errors such as inconsistent display content in each window.

In the computer system, there are also the Windows recycle bin, the file system in the operating system, the thread pool in multi-threading , the driver object of the graphics card, the background processing service of the printer, the log object of the application, the connection pool of the database, and the website Counters, configuration objects of web applications, dialog boxes in applications, caches in the system, etc. are often designed as singletons.

The singleton pattern has three 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 the singleton;

2. Structure and implementation of singleton pattern

The singleton pattern is one of the simplest design patterns . Usually, the constructor of an ordinary class is public, and external classes can generate multiple instances through "new constructor()". However, if you make a class's constructor private, external classes cannot call the constructor and cannot generate multiple instances. At this time, the class itself must define a static private instance and provide a static public function to create or obtain the static private instance.

Let's analyze its basic structure and implementation method.

2.1. Structure of singleton pattern

The main roles of the singleton pattern are as follows.

  • Singleton class: A class that contains one instance and can create this instance by itself.
  • Access class: A class that uses a singleton.

Its structure is shown in Figure 1.

2.2. Implementation of singleton pattern

There are usually two implementations of the Singleton pattern.

2.3.1. Lazy Singleton

The characteristic of this mode is that no singleton is generated when the class is loaded. This singleton is only created when the getlnstance method is called for the first time. code show as below:

public class LazySingleton
{
    private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
    private LazySingleton(){}    //private 避免类在外部被实例化
    public static synchronized LazySingleton getInstance()
    {
        //getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
}

Note: If you are writing a multi-threaded program, do not delete the keywords volatile and synchronized in the above example code, otherwise there will be thread non-safety issues. If these two keywords are not deleted, thread safety can be guaranteed, but synchronization is required every time it is accessed, which will affect performance and consume more resources. This is the shortcoming of the lazy singleton.

2.3.2. Hungry Han style singleton

The characteristic of this mode is that a singleton is created once the class is loaded, ensuring that the singleton already exists before the getInstance method is called.

public class HungrySingleton
{
    private static final HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance()
    {
        return instance;
    }
}

The Hungry-style singleton has already created a static object for system use when the class is created, and will not change it in the future. Therefore, it is thread-safe and can be directly used in multi-threading without problems.

3. Eight ways to write singleton mode

3.1. Hungry Han style singleton

Writing method one: 

package com.company.singleton;

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 * (话说你不用的,你装载它干啥)
 */
public class Mgr01 {

    private static final Mgr01 INSTANCE = new Mgr01();

    private Mgr01() {};

    public static Mgr01 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr01 m1 = Mgr01.getInstance();
        Mgr01 m2 = Mgr01.getInstance();
        System.out.println(m1 == m2);
    }
}

Writing method two:

package com.company.singleton;

/**
 * 跟01是一个意思
 */
public class Mgr02 {
    private static final Mgr02 INSTANCE;
    static {
        INSTANCE = new Mgr02();
    }

    private Mgr02() {};

    public static Mgr02 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr02 m1 = Mgr02.getInstance();
        Mgr02 m2 = Mgr02.getInstance();
        System.out.println(m1 == m2);
    }
}

3.2. Lazy Singleton

Writing method one;

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 */
public class Mgr03 {
    private static Mgr03 INSTANCE;

    private Mgr03() {
    }

    public static Mgr03 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->
                System.out.println(Mgr03.getInstance().hashCode())
            ).start();
        }
    }
}

Writing method two;

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr04 {
    private static Mgr04 INSTANCE;

    private Mgr04() {
    }

    public static synchronized Mgr04 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr04.getInstance().hashCode());
            }).start();
        }
    }
}

Writing method three:

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr05 {
    private static Mgr05 INSTANCE;

    private Mgr05() {
    }

    public static Mgr05 getInstance() {
        if (INSTANCE == null) {
            //妄图通过减小同步代码块的方式提高效率,然后不可行
            synchronized (Mgr05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr05();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr05.getInstance().hashCode());
            }).start();
        }
    }
}

Writing method four:

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr06 {
    private static volatile Mgr06 INSTANCE; //JIT

    private Mgr06() {
    }

    public static Mgr06 getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (Mgr06.class) {
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr06();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr06.getInstance().hashCode());
            }).start();
        }
    }
}

Writing method five:

package com.company.singleton;

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Mgr07 {

    private Mgr07() {
    }

    private static class Mgr07Holder {
        private final static Mgr07 INSTANCE = new Mgr07();
    }

    public static Mgr07 getInstance() {
        return Mgr07Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<1000; i++) {
            new Thread(()->{
                System.out.println(Mgr07.getInstance().hashCode());
            }).start();
        }
    }


}

Writing method six:

package com.company.singleton;

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 */
public enum Mgr08 {

    INSTANCE;

    public void m() {}

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr08.INSTANCE.hashCode());
            }).start();
        }
    }

}

4. Application examples of singleton pattern

[Example 1] Use the lazy singleton pattern to simulate the generation of the current president of the United States object.

Analysis: During each term, there is only one president of the United States, so this example is suitable for implementation using the singleton model. Figure 2 shows the structure diagram implemented using the lazy-style singleton.

Figure 2 Structural diagram of the US President generator 

The program code is as follows:

public class SingletonLazy
{
    public static void main(String[] args)
    {
        President zt1=President.getInstance();
        zt1.getName();    //输出总统的名字
        President zt2=President.getInstance();
        zt2.getName();    //输出总统的名字
        if(zt1==zt2)
        {
           System.out.println("他们是同一人!");
        }
        else
        {
           System.out.println("他们不是同一人!");
        }
    }
}
class President
{
    private static volatile President instance=null;    //保证instance在所有线程中同步
    //private避免类在外部被实例化
    private President()
    {
        System.out.println("产生一个总统!");
    }
    public static synchronized President getInstance()
    {
        //在getInstance方法上加同步
        if(instance==null)
        {
               instance=new President();
        }
        else
        {
           System.out.println("已经有一个总统,不能产生新总统!");
        }
        return instance;
    }
    public void getName()
    {
        System.out.println("我是美国总统:特朗普。");
    }  
}

The results of running the program are as follows:

Produce a president!
I am the President of the United States: Trump.
There is already a president and a new president cannot be produced!
I am the President of the United States: Trump.
They are the same person!

[Example 2] Use Hungry-style singleton mode to simulate generating Zhubajie objects.

Analysis: Similar to the above example, there is only one Zhu Bajie, so this example is also suitable for implementation in the singleton mode. In this example, since the image of Zhu Bajie is to be displayed ( click here to download the image of Zhu Bajie to be displayed by the program ), the frame form JFrame component is used. The Zhu Bajie class here is a singleton class, which can be defined as a subclass of the panel JPanel. It contains tags for saving the image of Zhu Bajie. The client form can obtain the Zhu Bajie object and display it. Figure 3 shows the structure diagram implemented using Hungry-style singleton.

Figure 3 Structural diagram of Zhubajie generator

The program code is as follows:

import java.awt.*;
import javax.swing.*;
public class SingletonEager
{
    public static void main(String[] args)
    {
        JFrame jf=new JFrame("饿汉单例模式测试");
        jf.setLayout(new GridLayout(1,2));
        Container contentPane=jf.getContentPane();
        Bajie obj1=Bajie.getInstance();
        contentPane.add(obj1);    
        Bajie obj2=Bajie.getInstance(); 
        contentPane.add(obj2);
        if(obj1==obj2)
        {
            System.out.println("他们是同一人!");
        }
        else
        {
            System.out.println("他们不是同一人!");
        }   
        jf.pack();       
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
class Bajie extends JPanel
{
    private static Bajie instance=new Bajie();
    private Bajie()
    { 
        JLabel l1=new JLabel(new ImageIcon("src/Bajie.jpg"));
        this.add(l1);   
    }
    public static Bajie getInstance()
    {
        return instance;
    }
}

The program running results are shown in Figure 4.

Figure 4 Running results of Zhubajie generator

5. Application scenarios of singleton mode 

The structure and characteristics of the singleton pattern have been analyzed before. The following are the characteristics of the scenarios it is usually applicable to.

  • In application scenarios, when a certain type only requires the generation of one object, such as the monitor of a class, each person's ID number, etc.
  • When objects need to be shared. Since the singleton pattern allows only one object to be created, sharing the object can save memory and speed up object access. Such as configuration objects in the Web, database connection pools, etc.
  • When a certain class needs to be instantiated frequently and the created objects are frequently destroyed, such as multi-threaded thread pools, network connection pools, etc.

6. Extension of Singleton Pattern

The singleton mode can be extended to the limited multi-instance (Multitcm) mode. This mode can generate a limited number of instances and save them in an ArrayList, which can be obtained randomly by customers when needed. The structure diagram is shown in Figure 5.

Figure 5 Structural diagram of limited multiple instance mode 

Reference article:  Detailed explanation of singleton pattern (single case design pattern)_smile_and_ovo's blog-CSDN blog_single case pattern

Guess you like

Origin blog.csdn.net/m0_50370837/article/details/126179908