Singleton mode (the most easy-to-understand in history)

Singleton pattern is better than design pattern

Reading guide

  • This article will explain from the requirements, design, advantages and disadvantages
  • Understanding thinking is more important than coding!

Singleton mode


1 Introduction

   Singleton Pattern (Singleton Pattern) is one of the simplest design patterns in Java, generally speaking, it is a single instance of a class. This type of design pattern belongs to the creation pattern, which provides the best way to create objects.

   This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique object, which can be accessed directly without instantiating objects of this class.


In general

  • A singleton class can only have one instance (static)
  • Singleton class can only create its own unique instance (private modification construction method, refer to enum class)
  • The singleton class must provide this instance to all other objects (get the instance through a custom public method)

2. A superficial understanding of singletons from requirements and purposes
  • Intent : To ensure that a class has only one instance, and to provide its global access point.
  • Purpose : Avoid frequent creation and destruction of frequently used classes (spring default creation of beans is a singleton)
  • Disadvantages : No interface, no inheritance.

Note: Students who are interested can learn about the singleton and inheritable beans created by spring


3. Initial realization of singleton (hungry man)


Steps to create the singleton class itself

  • Private static creation of an instance (staticity guarantees only instance)
  • Private constructor (the class is not instantiated, only the class itself can be instantiated)
  • Public static method to get the object

The reference code is as follows

/**
* 创建单例对象
**/
public class SingleonBean {
    
    

	// 创建 SingleObject 的一个对象
	private static SingleonBean instance = new SingleonBean();

	// 让构造函数为 private,这样该类就不会被实例化
	private SingleonBean() {
    
    
	}

	// 获取唯一可用的对象
	public static SingleonBean getInstance() {
    
    
		return instance;
	}

	public void doSomething() {
    
    
		System.out.println("Do Something");
	}
}

Caller

  • Only through (class name. public method) to obtain this class instance.

Note: It is equivalent to writing a tool class, we set the method of this class to static, then we can use the class name. Method name to call this method

The reference code is as follows

/**
* 获取使用单例对象
**/
public class GetSingleonBean {
    
    

	public static void main(String[] args) {
    
    
				
		// 编译器报错 "The constructor SingleonBean() is not visible"
		// 不合法的构造函数,因为这个构造函数是private修饰不可见的
		// SingleonBean singleonBean = new SingleonBean(); <------这是被注释的代码
		
		// 正确获取唯一单例方式
		SingleonBean instance = SingleonBean.getInstance();
		
		// 用单例做一些事情
		instance.doSomething();
	}
}

4. Singleton advanced realization (interview hot spots)


Realization 1 (lazy man)
  • Thread unsafe
  • Lazy loading (load when used to avoid wasting resources)
  • Strictly not a singleton (multithreading is not safe)

It is our basic code implementation that realizes lazy loading.

Code

/**
 * 
 * @author fudy 构造懒汉不安全单例模式
 */
public class SingleonBean {
    
    

	// 创建 SingleObject 的一个对象延迟初始化
	private static SingleonBean instance;

	// 让构造函数为 private,这样该类就不会被实例化
	private SingleonBean() {
    
    
	}

	// 获取唯一可用的对象
	public static SingleonBean getInstance() {
    
    
		
		// 此处会有线程问题,如果线程A和线程B都执行到了步骤1,并未完成步骤2,那么就会创建多个实例
		if (instance == null) {
    
       // 步骤1
			 instance = new SingleonBean(); // 步骤2
		}
		return instance;
	}

	public void doSomething() {
    
    
		System.out.println("Do Something");
	}
}

Implementation 2 (safe lazy style)
  • Thread safe
  • Lazy loading

Since we realize 1 thread is not safe, we think that the best way is to add synchronized to ensure safety, and at the same time locking will affect the efficiency, every time the method is accessed, the synchronized code block will be entered.

Code

/**
 * 
 * @author fudy 构造懒汉不安全单例模式
 */
public class SingleonBean {
    
    

	// 创建 SingleObject 的一个对象延迟初始化
	private static SingleonBean instance;

	// 让构造函数为 private,这样该类就不会被实例化
	private SingleonBean() {
    
    }

	// 加重量级锁获取唯一可用的对象(每次访问该方法都会进入同步代码块)
	public static synchronized  SingleonBean getInstance() {
    
    	
		if (instance == null) {
    
       
			 instance = new SingleonBean();
		}
		return instance;
	}

	public void doSomething() {
    
    
		System.out.println("Do Something");
	}
}

Realization 3 (hungry Chinese style, more used)

Our basic realization is to use a hungry man, you can refer to the preliminary realization of the third step of this article.


Implementation 4 (Double check lock)
  • Lazy loading
  • Safety
  • Starting from jdk5, (5 version optimizes volatile)

When we use implementation 1, because getInstance may create multiple singletons under multi-threading, we lock getInstance in implementation 2. At this time, security can be guaranteed. If this class is accessed more frequently, it will cause performance degradation. We implement double check locks to narrow the scope of synchronized code blocks, and use volatile to ensure that the execution order is not disrupted (instruction reordering)

Code

/**
 * 
 * @author fudy 构造懒汉不安全单例模式
 */
public class SingleonBean {
    
    

	// 使用volatile修饰可以保证禁止指令重排序
	private volatile static SingleonBean instance;

	// 让构造函数为 private,这样该类就不会被实例化
	private SingleonBean() {
    
    }

	// 获取唯一可用的对象
	public static   SingleonBean getInstance() {
    
    
		// 如果单例我们就开始创建单例(步骤1)
		if (instance == null) {
    
    
			// 此时锁定类,我们缩小同步代码块的范围(步骤2)
			synchronized(SingleonBean.class){
    
    
				// 在锁定类的同时,有可能有其他的线程已经执行到步骤3,
				// 但未执行完,我们需要再判断是否其他线程已经执行完步骤3
				if (instance == null) {
    
     
					 instance = new SingleonBean(); // 实例化对象(步骤3)
				}
			}
		}
		return instance;
	}

	public void doSomething() {
    
    
		System.out.println("Do Something");
	}
}

Implementation 5 (static inner class)
  • Initial loading
  • Thread safe

By implementing 3, we found that it is not lazy loading, but hungry loading. We have another idea for lazy loading. Call the static internal class and use the classloader mechanism to ensure that there is only one thread when the instance is initialized.

Code

/**
 * 静态内部类实现单例
 */
public class SingleBean {
    
    
	
	// 设置静态内部类,在使用时再加载其内容
	private static class PrivateSignleBean{
    
    
		// 使用final修饰后,对象引用不能改变
		private final static SingleBean INSTANCE = new SingleBean();
	}
	// 私有构造方法不能实例化对象
	private  SingleBean(){
    
    
		
	}
	// 获取单例对象
	public static final SingleBean getInstance(){
    
    
		return PrivateSignleBean.INSTANCE;
	}

}

Implementation 6 (enumeration)

I didn't understand it completely, and interested students can find out.


Guess you like

Origin blog.csdn.net/qq_44112474/article/details/108229512