What you don’t know in singleton mode~~

It can be said that the singleton mode can be written as long as it is a qualified development, but if we want to study deeply, a small singleton mode can involve many things, such as: is multithreading safe? Lazy load? Performance, etc. Also, do you know how to write several singleton modes? How to prevent reflection from breaking singleton mode?

1、 Singleton mode

1.1 definitions

Singleton mode is to instantiate a program only once to create a globally unique object. It is a bit like static variables in Java, but the singleton mode is better than static variables:

Many tool classes in the development tool class library apply the singleton mode, such as proportional thread pool, cache, log objects, etc. they only need to create one object. If multiple instances are created, it may bring unpredictable problems, such as waste of resources, inconsistent result processing, etc.

1.2 implementation idea of single example

1.3 benefits of single case

2、 Implementation of singleton mode

2.1 hungry Han mode

When defining static properties, the object is instantiated directly

public class HungryMode {

    /** * 利用静态变量来存储唯一实例 */ private static final HungryMode instance = new HungryMode(); /** * 私有化构造函数 */ private HungryMode(){ // 里面可以有很多操作 } /** * 提供公开获取实例接口 * @return */ public static HungryMode getInstance(){ return instance; } }

2.1. 1 advantages

The static keyword is used to ensure that all write operations on this variable are completed when referencing this variable, so thread safety at the JVM level is guaranteed

2.1. 2 disadvantages

Lazy loading cannot be realized, resulting in a waste of space: if a class is relatively large, we loaded it during initialization, but we haven't used it for a long time, which leads to a waste of memory space.

2.2 lazy mode

Lazy mode is a lazy mode. Instances will not be created during program initialization, but only when using instances. Therefore, lazy mode solves the problem of space waste caused by hungry mode.

2.2. 1 general implementation of lazy mode

public class LazyMode {
    /** * 定义静态变量时,未初始化实例 */ private static LazyMode instance; /** * 私有化构造函数 */ private LazyMode(){ // 里面可以有很多操作 } /** * 提供公开获取实例接口 * @return */ public static LazyMode getInstance(){ // 使用时,先判断实例是否为空,如果实例为空,则实例化对象 if (instance == null) { instance = new LazyMode(); } return instance; } }

However, this implementation is not safe in the case of multithreading, and multiple instances may occur:

if (instance == null) {
    instance = new LazyMode();
}

Suppose two threads enter the above code at the same time. Because there are no resource protection measures, both threads can judge that the instances are empty at the same time and will initialize the instances. Therefore, multiple instances will occur.

2.2. 2. Optimization of lazy mode

We add the synchronized keyword to the getInstance () method to make the getInstance () method a protected resource, which can solve the problem of multiple instances.

public class LazyModeSynchronized {
    /** * 定义静态变量时,未初始化实例 */ private static LazyModeSynchronized instance; /** * 私有化构造函数 */ private LazyModeSynchronized(){ // 里面可以有很多操作 } /** * 提供公开获取实例接口 * @return */ public synchronized static LazyModeSynchronized getInstance(){ /** * 添加class类锁,影响了性能,加锁之后将代码进行了串行化, * 我们的代码块绝大部分是读操作,在读操作的情况下,代码线程是安全的 * */ if (instance == null) { instance = new LazyModeSynchronized(); } return instance; } }

2.2. 3 advantages of lazy mode

Lazy loading is realized and memory space is saved.

2.2. 4 disadvantages of lazy mode

The problem of locking in lazy mode. For getInstance () method, most operations are read operations. Read operations are thread safe, so we don't have to make each thread hold a lock to call the method. We need to adjust the problem of locking. This also produces a new implementation mode: double check lock mode.

2.3 double check lock mode

2.3. 1. General implementation of double check lock mode

public class DoubleCheckLockMode {

    private static DoubleCheckLockMode instance; /** * 私有化构造函数 */ private DoubleCheckLockMode(){ } /** * 提供公开获取实例接口 * @return */ public static DoubleCheckLockMode getInstance(){ // 第一次判断,如果这里为空,不进入抢锁阶段,直接返回实例 if (instance == null) { synchronized (DoubleCheckLockMode.class) { // 抢到锁之后再次判断是否为空 if (instance == null) { instance = new DoubleCheckLockMode(); } } } return instance; } }

The double check lock mode solves the problems of singleton, performance and thread safety, but this writing method also has problems: in the case of multithreading, the null pointer problem may occur. The reason for the problem is that the JVM will optimize and reorder instructions when instantiating objects.

2.3. 2 what is instruction rearrangement?

private SingletonObject(){
      // 第一步 int x = 10; // 第二步 int y = 30; // 第三步 Object o = new Object(); }

The above constructor singletonobject(), the JVM will reorder its instructions, so the execution order may be out of order. However, regardless of the execution order, the JVM will ensure that all instances are instantiated in the end. If there are many operations in the constructor, in order to improve efficiency, the JVM will return the object when all the properties in the constructor are not instantiated. The reason for the null pointer problem of double detection lock is here. When a thread obtains the lock for instantiation, other threads directly obtain the instance for use. Due to the reordering of JVM instructions, the object obtained by other threads may not be a complete object, so the null pointer exception problem will occur when using the instance.

2.3. 3 double check lock mode optimization

To solve the problem of null pointer exception caused by double check lock mode, you only need to use volatile keyword. Volatile keyword strictly follows the happens before principle, that is, all write operations must be completed before read operation.

public class DoubleCheckLockModelVolatile {
    /** * 添加volatile关键字,保证在读操作前,写操作必须全部完成 */ private static volatile DoubleCheckLockModelVolatile instance; /** * 私有化构造函数 */ private DoubleCheckLockModelVolatile(){ } /** * 提供公开获取实例接口 * @return */ public static DoubleCheckLockModelVolatile getInstance(){ if (instance == null) { synchronized (DoubleCheckLockModelVolatile.class) { if (instance == null) { instance = new DoubleCheckLockModelVolatile(); } } } return instance; } }

2.4 static internal class mode

Static internal class mode is also known as singleton holder mode. Instances are created by internal classes. Because the JVM will not load static internal classes in the process of loading external classes, only the properties / methods of internal classes will be loaded and their static properties will be initialized. Static attributes are modified by static to ensure that they are instantiated only once, and the instantiation order is strictly guaranteed.

public class StaticInnerClassMode {

    private StaticInnerClassMode(){ } /** * 单例持有者 */ private static class InstanceHolder{ private final static StaticInnerClassMode instance = new StaticInnerClassMode(); } /** * 提供公开获取实例接口 * @return */ public static StaticInnerClassMode getInstance(){ // 调用内部类属性 return InstanceHolder.instance; } }

This method is similar to the mechanism adopted by the hungry Han method, but it is different. Both adopt the mechanism of class loading to ensure that there is only one thread when initializing the instance. Different places:

The static properties of the class will only be initialized when the class is loaded for the first time. Therefore, the JVM helps us ensure the safety of threads. When the class is initialized, other threads cannot enter.

Therefore, this method ensures the safety of multithreading without any lock, and has no performance impact and waste of space.

2.5 enumeration class implements singleton mode

Because the enumeration type is thread safe and can only be loaded once, the designer makes full use of this feature of enumeration to implement the singleton mode. The writing method of enumeration is very simple, and the enumeration type is the only singleton implementation mode that will not be destroyed.

public class EnumerationMode {
    
    private EnumerationMode(){ } /** * 枚举类型是线程安全的,并且只会装载一次 */ private enum Singleton{ INSTANCE; private final EnumerationMode instance; Singleton(){ instance = new EnumerationMode(); } private EnumerationMode getInstance(){ return instance; } } public static EnumerationMode getInstance(){ return Singleton.INSTANCE.getInstance(); } }

Applicable occasions:

3、 Problems and solutions of singleton mode

Except enumeration, other methods will destroy the singleton by reflection

3.1 destruction of single case mode

/**
 * 以静态内部类实现为例
 * @throws Exception
 */
@Test
public void singletonTest() throws Exception { Constructor constructor = StaticInnerClassMode.class.getDeclaredConstructor(); constructor.setAccessible(true); StaticInnerClassMode obj1 = StaticInnerClassMode.getInstance(); StaticInnerClassMode obj2 = StaticInnerClassMode.getInstance(); StaticInnerClassMode obj3 = (StaticInnerClassMode) constructor.newInstance(); System.out.println("输出结果为:"+obj1.hashCode()+"," +obj2.hashCode()+","+obj3.hashCode()); }

Console printing:

输出结果为:1454171136,1454171136,1195396074

From the output results, we can see that obj1 and obj2 are the same object and obb3 is a new object. Obj3 is that we call the private constructor through the reflection mechanism, and then generate a new object.

3.2 how to prevent single failure

You can judge in the construction method. If there is an existing instance, prevent the generation of a new instance. The solution is as follows:

public class StaticInnerClassModeProtection {

    private static boolean flag = false; private StaticInnerClassModeProtection(){ synchronized(StaticInnerClassModeProtection.class){ if(flag == false){ flag = true; }else { throw new RuntimeException("实例已经存在,请通过 getInstance()方法获取!"); } } } /** * 单例持有者 */ private static class InstanceHolder{ private final static StaticInnerClassModeProtection instance = new StaticInnerClassModeProtection(); } /** * 提供公开获取实例接口 * @return */ public static StaticInnerClassModeProtection getInstance(){ // 调用内部类属性 return InstanceHolder.instance; } }

Test:

/**
 * 在构造方法中进行判断,若存在则抛出RuntimeException
 * @throws Exception
 */
@Test
public void destroyTest() throws Exception { Constructor constructor = StaticInnerClassModeProtection.class.getDeclaredConstructor(); constructor.setAccessible(true); StaticInnerClassModeProtection obj1 = StaticInnerClassModeProtection.getInstance(); StaticInnerClassModeProtection obj2 = StaticInnerClassModeProtection.getInstance(); StaticInnerClassModeProtection obj3 = (StaticInnerClassModeProtection) constructor.newInstance(); System.out.println("输出结果为:"+obj1.hashCode()+","+obj3.hashCode()); }

Console printing:

Caused by: java.lang.RuntimeException: 实例已经存在,请通过 getInstance()方法获取! at cn.van.singleton.demo.mode.StaticInnerClassModeProtection.<init>(StaticInnerClassModeProtection.java:22) ... 35 more

4、 Summary

4.1 comparison of various implementations

Reprint the original link: https://www.cnblogs.com/vandusty/p/11444293.html

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>