23 design patterns - singleton pattern (java implementation)

1. Definition and its advantages and disadvantages

Singleton mode: The entire program has one and only one instance; this class is responsible for creating its own objects while ensuring that only one object is created.

Generally, the following points are required to implement the singleton pattern:

  1. Class constructor is private (generally parameterless construction)
  2. Hold private properties of your own type (private static Singleton instance;)
  3. Provides a static method to obtain the instance (public static Singleton getInstance())

advantage:

  • Singleton mode has only one instance in memory, reducing memory overhead. (Especially when an object needs to be created and destroyed frequently)
  • Singleton mode only generates one instance, reducing system performance overhead. (When an object requires a lot of resources to generate, such as reading configuration and generating other dependent objects, it can be solved by directly generating a singleton object when the application starts, and then permanently resident in memory)
  • The singleton mode can avoid multiple occupation of resources. (For example, for a file writing operation, since only one instance exists in the memory, simultaneous writing operations on the same resource file are avoided)
  • Singleton mode can set global access points in the system to optimize and share resource access. (For example, you can design a singleton class that is responsible for the mapping processing of all data tables)

shortcoming:

  • The singleton pattern has no abstraction layer and is difficult to extend; if you want to extend it, there is basically no other way to achieve it except modifying the code.
  • It is not suitable for changing objects. If objects of the same type always change in different use case scenarios, singletons will cause data errors and cannot save each other's states.
  • Abuse of singletons will bring some negative problems: if the instantiated object is not used for a long time, the system will consider it garbage and be recycled, which will lead to the loss of object state.

 2. Several implementations of the singleton pattern and their advantages and disadvantages

1. Hungry Man Mode: Singleton objects are non-delayed loading, thread-safe, and commonly used, but are prone to generating garbage because they are initialized at the beginning.

public class HungryManSingleton {
	//1、私有化构造器
	private HungryManSingleton() {
	}	
	//2、持有自己类型的私有属性(类加载时,天然的线程安全,类初始化时,立即加载这个对象(唯一一个),缺乏延时加载的优势)
	private static HungryManSingleton instance = new HungryManSingleton();
	//3、暴露创建对象的方法(方法没有加同步,因此调用效率高)
	public static HungryManSingleton getInstance() {
		return instance;
	}
}

2. Lazy mode (thread safety mode): implements delayed loading (lazy loading) and thread safety, but has low efficiency and must be synchronized every time an instance is generated.

public class LazyManSingleton {
	//1、私有化构造器
	private LazyManSingleton() {
	}	
	//2、持有自己类型的私有属性,类初始化时,不初始化这个对象(延迟加载,真正用时再创建)
	private static LazyManSingleton instance;
	//3、暴露创建对象的方法(方法同步,因此调用效率低)
	public static synchronized LazyManSingleton getInstance() {
		if(null == instance) {
			instance = new LazyManSingleton();
		}
		return instance;
	}
}

3. Double check lock mode: thread safety, delayed initialization, high efficiency

public class DoubleCheckSingleton {
	// 私有化构造器
	private DoubleCheckSingleton() {}
	// volatile关键字禁止指令重排
	private volatile static DoubleCheckSingleton instance;
	// 双重检查锁
	public static DoubleCheckSingleton getInstance() {
		if (instance == null) {
			synchronized (DoubleCheckSingleton.class) {
				if (instance == null) {
					instance = new DoubleCheckSingleton();
				}
			}
		}
		return instance;
	}
}

The double-check lock mode makes double judgments when creating an instance. The first layer of judgment is to avoid re-entering synchronization when an instance already exists. Because it avoids the synchronization operation except for the first instance creation, the efficiency is improved; the second layer The purpose of judgment is to ensure synchronized operations under multi-threads and prevent the creation of multiple instances.

Due to the internal optimization mechanism of the JVM, it is possible that the instance = new DoubleCheckSingleton() object will be reordered when it is created (the object creation process is very complicated, and the JVM will reorder the instructions during its creation process, resulting in early release of the lock), so many There are risks in this mode under threads; the volatile keyword prohibits instruction reordering to solve this problem.

4. Static inner classes implement singleton mode: thread safety, high calling efficiency, delayed loading (better than lazy mode, can be preferred)

public class StaticInnerClassSingleton {
	//1、私有化构造器
	private StaticInnerClassSingleton() {}	
	//2、静态内部类(延迟加载)
	private static class Singleton{
		private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
	}
	//3、暴露创建对象的方法,直接返回内部类的对象(内部类加载,天然线程安全)
	public static StaticInnerClassSingleton getInstance() {
		return Singleton.instance;
	}
}

5. Enumeration implements singleton mode: thread safety, high calling efficiency, avoiding reflection and deserialization vulnerabilities (non-delayed loading, better than hungry Chinese style)

public enum EnumSingleton {
	//枚举元素,本身就是单例对象
	INSTANCE;
	//操作枚举元素即可
	public void singletonOperation() {}
}

3. Prevent reflection and deserialization from cracking singleton mode (enumeration naturally protects this vulnerability)

1. Reflection can use constructor.setAccessible(true); to skip the private protection and call the private constructor, so you can use reflection to generate new instances. To prevent it from breaking the singleton mode, restrictions can be placed in the constructor. The code is as follows:

public class LazyManSingletonCrack {
	//1、私有化构造器
	private LazyManSingletonCrack() {
		//防止反射破解单例模式
		if (instance != null) {    
                //若已经创建实例,主动抛出异常
			throw new RuntimeException();
		}
	}	
	//2、持有自己类型的私有属性,类初始化时,不初始化这个对象(延迟加载,真正用时再创建)
	private static LazyManSingletonCrack instance;
	//3、暴露创建对象的方法(方法同步,因此调用效率低)
	public static synchronized LazyManSingletonCrack getInstance() {
		if(null == instance) {
			instance = new LazyManSingletonCrack();
		}
		return instance;
	}
}

2. When deserializing, a new instance will be created to receive the original serialized instance written to the hard disk, and then the singleton mode will be cracked; to prevent it from being cracked, the readResolve() method can be defined in the singleton mode, so that The instance remains unique, the code is as follows:

public class LazyManSingletonCrack implements Serializable{
	//1、私有化构造器
	private LazyManSingletonCrack() {}	
	//2、持有自己类型的私有属性
	private static LazyManSingletonCrack instance;
	//3、暴露创建对象的方法
	public static synchronized LazyManSingletonCrack getInstance() {
		if(null == instance) {
			instance = new LazyManSingletonCrack();
		}
		return instance;
	}
	//反序列化时,如果定义了readResolve,可以将指定对象返回,不会返回反序列化后的新对象
	private Object readResolve() throws ObjectStreamException{
		return instance;
	}
}

4. Test the performance of various singleton modes

1. Test the efficiency of different singleton modes in a multi-threaded environment, and obtain the test results in the local running environment. The test code is as follows

public class TestEfficiency {
	public static void main(String[] args) throws Exception {
		long start = System.currentTimeMillis();
		int threadNum = 10;//线程数量
		//为了保证线程都执行完,再计算时间,使用线程计数器CountDownLatch,内部类不能调用局部变量,因此声明为final常量
		final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
		//new threadNum 个线程
		for (int i = 0; i < threadNum; i++) {
			//内部类方式实现多线程
			new Thread(new Runnable() {
				@Override
				public void run() {
					//循环创建100000次
					for (int i = 0; i < 1000000; i++) {
						Object obj = LazyManSingleton.getInstance();
					}
					//线程结束后计数器减一
					countDownLatch.countDown();
				}
			}).start();
		}
		//main线程进行等待(阻塞),直到所有线程结束
		countDownLatch.await();
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
}

2. Test the efficiency of different singleton modes in a multi-threaded environment. The test results are as follows

Hungry Chinese style 25ms
lazy man style 139ms
double check lock 46ms
static inner class 31ms
enumerate 39ms

 

Guess you like

Origin blog.csdn.net/Jeff_fei/article/details/103332953