Singleton, you really write on it?

Look at the company code when the project was found in a lot of applications Singleton, Singleton pattern and found two used or implemented differently, then the Singleton pattern in the end there are several written it? Singleton pattern looks simple, but it was actually written a lot of problems

This article outlines

  • What is a singleton
  • Starving singleton object type to create
  • Create a singleton object lazy style
  • The advantages and disadvantages of singleton
  • Examples of single-mode application scenario

What is a singleton

Make sure that a class has only one instance , and to instantiate and provide the example of the whole system, and there are two ways to create a Chinese-style creation is hungry, another is the lazy man's creation

Hungry man style create a singleton

Hungry man is the type to create objects had been created when the class is loaded, and not when needed to create objects

public class HungrySingleton {
    private static HungrySingleton hungrySingleton = new HungrySingleton();

    /**
     * 私有构造函数,不能被外部所访问
     */
    private HungrySingleton() {}

    /**
     * 返回单例对象
     * */
    public static HungrySingleton getHungrySingleton() {
        return hungrySingleton;
    }
}
复制代码

Description:

  • Constructors privatization, to ensure the outside can not call the constructor to create an object, create an object's behavior can only be determined by the class
  • Only by getHungrySingletonacquiring object methods
  • HungrySingletonObject has been created [created when the class loader]

Disadvantages:

  • If you getHungrySingletonhave not used to, a waste of resources

advantage:

  • By the ClassLoadguarantee thread safety

Create singleton lazy formula

Lazy man's creation is the first time you need to create the object

  • Lazy singleton object type created with errors
    could be easily modified on the basis of the above starving the definition of formula

    public class LazySingleton {
        private static LazySingleton lazySingleton = null;
    
        /**
         * 构造函数私有化
         * */
        private LazySingleton() {
        }
    
        private static LazySingleton getLazySingleton() {
            if (lazySingleton == null) {
                return new LazySingleton();
            }
          
            return lazySingleton;
        }
    }
    复制代码

    Description:

    • Constructors privatization
    • When you need [ getLazySingletontime] was to create a method call ah, it seems no problem, but when there are multiple threads simultaneously call getLazySingletonupon the method, the object is not initialized at this time just two threads simultaneously by lazySingleton == nullcheck, it will create two LazySingletonobjects. Means must point out the getLazySingletonmethod is thread safe
  • synchronizeOr Lock
    it is easy to think of using synchronizeor Lockmethod for locking
    use synchronize:

    public class LazySynchronizeSingleton {
        private static LazySynchronizeSingleton lazySynchronizeSingleton= null;
      
        /**
         * 构造函数私有化
         * */
        private LazySynchronizeSingleton() {
        }
    
        public synchronized static LazySynchronizeSingleton getLazySynchronizeSingleton() {
            if (lazySynchronizeSingleton == null) {
                lazySynchronizeSingleton = new LazySynchronizeSingleton();
            }
          
            return lazySynchronizeSingleton;
        }
    }
    复制代码

    Use Lock:

    public class LazyLockSingleton {
        private static LazyLockSingleton lazyLockSingleton = null;
    
        /**
        * 锁
        **/
        private static Lock lock = new ReentrantLock();
    
        /**
         * 构造函数私有化
         * */
        private LazyLockSingleton() {
        }
    
        public static LazyLockSingleton getLazyLockSingleton() {
            try {
                lock.lock();
                if (lazyLockSingleton == null) {
                    lazyLockSingleton = new LazyLockSingleton();
                }
            } finally {
                lock.unlock();
            }
          
            return lazyLockSingleton;
        }
    }
    复制代码

    Although these two methods to ensure the security thread, but poor performance because the threads of insecurity is mainly caused by this code:

    if (lazyLockSingleton == null) {
      lazyLockSingleton = new LazyLockSingleton();
    }
    复制代码

    Method to lock regardless of whether the object has been initialized will cause the thread to block. If the object is nullonly to be locked case, the object is not nullthe time were not locked, then the performance will be enhanced and double check the lock can achieve this demand

  • Double lock check
    first before locking determine lazyDoubleCheckSingleton == nullwhether the establishment, if not established a direct return to the created objects, set up in lock

    public class LazyDoubleCheckSingleton {
        /**
         * 使用volatile进行修饰,禁止指令重排
         * */
        private static volatile LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    
        /**
         * 构造函数私有化
         * */
        private LazyDoubleCheckSingleton() {
        }
    
        public static LazyDoubleCheckSingleton getLazyDoubleCheckSingleton() {
            if (lazyDoubleCheckSingleton == null) {
                synchronized (LazyDoubleCheckSingleton.class) {
                    if (lazyDoubleCheckSingleton == null) {
                        lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                    }
                }
            }
          
            return lazyDoubleCheckSingleton;
        }
    }
    复制代码

    Description:

    • Why do we need to lazyDoubleCheckSingletonbe added volatileas a modifier lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();is not atomic, divided into three steps:
      • To lazyDoubleCheckSingletonallocate memory
      • Call the constructor to initialize
      • The lazyDoubleCheckSingletonobject pointing to the allocated memory [Executive completion of this step lazyDoubleCheckSingletonwill not null]

    In order to improve the efficiency of the program, the compiler will perform a rearrangement instruction, steps 2 and three have been rearranged, a first thread and a step of performing the three steps, executed, lazyDoubleCheckSingletonis not null, then the execution thread 2 if (lazyDoubleCheckSingleton == null), thread 2 will likely not return directly to initialize the correct lazyDoubleCheckSingletonobject. The main reason for the error is lazyDoubleCheckSingletonnot properly initialized completed [write], but other thread has read lazyDoubleCheckSingletonthe value of [reading], use volatilemay prohibit instruction reordering, through the memory barrier to ensure that does not call read before writing [Executive if (lazyDoubleCheckSingleton == null)]

    Disadvantages:

    • In order to ensure thread-safe code is not elegant too bloated
  • Static inner classes

    public class LazyStaticSingleton {
        /**
         * 静态内部类
         * */
        private static class LazyStaticSingletonHolder {
            private static LazyStaticSingleton lazyStaticSingleton = new LazyStaticSingleton();
        }
    
        /**
         * 构造函数私有化
         * */
        private LazyStaticSingleton() {
        }
    
        public static LazyStaticSingleton getLazyStaticSingleton() {
            return LazyStaticSingletonHolder.lazyStaticSingleton;
        }
    }
    复制代码

    Static inner classes will be initialized when you call, so is the lazy man, LazyStaticSingleton lazyStaticSingleton = new LazyStaticSingleton();appears to be a hungry man type, but only calls getLazyStaticSingletonwill be initialized when the thread-safe by the ClassLoadguarantee, without thinking how locked

In front of several ways to achieve a single way of example, although have advantages and disadvantages, but basically a singleton thread safety. But there are people who could not understand singleton thrift role, it carried out the attack. It is nothing more than an attack to create more than one class, javathe subject methods are created new, cloneserialization, reflection. The constructor can not be created through the privatization of new objects, while singleton class does not implement Cloneablethe interface by not clonecreating an object method, the rest of the attack that attacks only the reflection and serialization attacked
reflection attacks:

public class ReflectAttackTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //静态内部类
        LazyStaticSingleton lazyStaticSingleton = LazyStaticSingleton.getLazyStaticSingleton();
        //通过反射创建LazyStaticSingleton
        Constructor<LazyStaticSingleton> constructor = LazyStaticSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazyStaticSingleton lazyStaticSingleton1 = constructor.newInstance();
        //打印结果为false,说明又创建了一个新对象
        System.out.println(lazyStaticSingleton == lazyStaticSingleton1);

        //synchronize
        LazySynchronizeSingleton lazySynchronizeSingleton = LazySynchronizeSingleton.getLazySynchronizeSingleton();
        Constructor<LazySynchronizeSingleton> lazySynchronizeSingletonConstructor = LazySynchronizeSingleton.class.getDeclaredConstructor();
        lazySynchronizeSingletonConstructor.setAccessible(true);
        LazySynchronizeSingleton lazySynchronizeSingleton1 = lazySynchronizeSingletonConstructor.newInstance();
        System.out.println(lazySynchronizeSingleton == lazySynchronizeSingleton1);

        //lock
        LazyLockSingleton lazyLockSingleton = LazyLockSingleton.getLazyLockSingleton();
        Constructor<LazyLockSingleton> lazyLockSingletonConstructor = LazyLockSingleton.class.getConstructor();
        lazyLockSingletonConstructor.setAccessible(true);
        LazyLockSingleton lazyLockSingleton1 = lazyLockSingletonConstructor.newInstance();
        System.out.println(lazyLockSingleton == lazyLockSingleton1);

        //双重锁检查
        LazyDoubleCheckSingleton lazyDoubleCheckSingleton = LazyDoubleCheckSingleton.getLazyDoubleCheckSingleton();
        Constructor<LazyDoubleCheckSingleton> lazyDoubleCheckSingletonConstructor = LazyDoubleCheckSingleton.class.getConstructor();
        lazyDoubleCheckSingletonConstructor.setAccessible(true);
        LazyDoubleCheckSingleton lazyDoubleCheckSingleton1 = lazyDoubleCheckSingletonConstructor.newInstance();
        System.out.println(lazyDoubleCheckSingleton == lazyDoubleCheckSingleton1);
    }
}
复制代码

Based on static inner classes and synchronizecan create locked singleton object created by the reflection of the way the way the new object, there is reflection attacks, and the rest to create a singleton object several ways to use reflection to create new objects will be thrown

Deserialization attacks:

public class SerializableAttackTest {
    public static void main(String[] args) {
        //懒汉式
        HungrySingleton hungrySingleton = HungrySingleton.getHungrySingleton();
        //序列化
        byte[] serialize = SerializationUtils.serialize(hungrySingleton);
        //反序列化
        HungrySingleton hungrySingleton1 = SerializationUtils.deserialize(serialize);
        System.out.println(hungrySingleton == hungrySingleton1);

        //双重锁
        LazyDoubleCheckSingleton lazyDoubleCheckSingleton = LazyDoubleCheckSingleton.getLazyDoubleCheckSingleton();
        byte[] serialize1 = SerializationUtils.serialize(lazyDoubleCheckSingleton);
        LazyDoubleCheckSingleton lazyDoubleCheckSingleton11 = SerializationUtils.deserialize(serialize1);
        System.out.println(lazyDoubleCheckSingleton == lazyDoubleCheckSingleton11);

        //lock
        LazyLockSingleton lazyLockSingleton = LazyLockSingleton.getLazyLockSingleton();
        byte[] serialize2 = SerializationUtils.serialize(lazyLockSingleton);
        LazyLockSingleton lazyLockSingleton1 = SerializationUtils.deserialize(serialize2);
        System.out.println(lazyLockSingleton == lazyLockSingleton1);

        //synchronie
        LazySynchronizeSingleton lazySynchronizeSingleton = LazySynchronizeSingleton.getLazySynchronizeSingleton();
        byte[] serialize3 = SerializationUtils.serialize(lazySynchronizeSingleton);
        LazySynchronizeSingleton lazySynchronizeSingleton1 = SerializationUtils.deserialize(serialize3);
        System.out.println(lazySynchronizeSingleton == lazySynchronizeSingleton1);

        //静态内部类
        LazyStaticSingleton lazyStaticSingleton = LazyStaticSingleton.getLazyStaticSingleton();
        byte[] serialize4 = SerializationUtils.serialize(lazySynchronizeSingleton);
        LazyStaticSingleton lazyStaticSingleton1 = SerializationUtils.deserialize(serialize4);
        System.out.println(lazyStaticSingleton == lazyStaticSingleton1);

    }
}
复制代码

The results are printed false, deserialization attacks exist
for reflection attacks we can only choose to abandon the use of a single case of the existence of such defects way to create, but the counter-attack sequence, then it's still in the rescue, rescue posture as follows:

private Object readResolve() {
    return lazySynchronizeSingleton;
}
复制代码

Add readResolvemethod and return singleton object created, as the principle of rescue, you can follow SerializationUtils.deserializeknown code. The above-mentioned manner singleton object is necessary to consider thread safety, but also consider the attack, but by enumerating create singleton object nothing to worry about these issues

  • enumerate
    public enum EnumSingleton {
        INSTANCE;
    
        public static EnumSingleton getEnumSingleton() {
            return INSTANCE;
        }
    }
    复制代码
    Code implementation is quite beautiful, a total of only 8line on behalf of the
    realization of the principle: enumeration class field (field) is in fact the appropriate enum type of an instance of an object
    can refer to: implementingLearn-Singleton-with-AN-enum-in-the Java

Singleton pattern advantage

  • Only creates an instance to save memory overhead
  • Reducing the performance overhead of the system, create a certain impact on the performance of the object to be recovered has
  • Avoid multiple assignment of resources
  • In the system set global access points, optimize and share resources optimization

Summarize what is to save resources, improve performance

Disadvantage of singleton

  • Does not apply to objects for change
  • No single model abstraction layer embodiment, extended with difficulty
  • And the principle of a single conflict. A class should implement only a logical, not on whether it is a single case, is not a single case should be decided by the business

Examples of single-mode application scenario

  • Spring IOCSingleton pattern is created using the defaultbean
  • When you create an object needs to consume too many resources
  • Static constant need to define a large number of static methods and environments, such as tools [feeling] is the most common scenario

summary

A total of six introduced to create a singleton object right way, create recommended starving singleton object type way, if there is demand for resources, it is recommended to use a static inner classes [Note] deserialization attack, otherwise the guarantee thread-safe while performance will be affected. The enumeration class is actually very good, thread-safe, there is no reflection of attack and counter-attack sequence, but feel this way create less singleton application, the company code is used to check and double lock there is anti-static inner classes [ serialization attack] to create a single way of example, when the interviewer before the interview even let out to write a single example, I use the enumeration way, the interviewer did not know there is this way

Last Attachment: Full test code example code +

Welcome fork with star

Guess you like

Origin juejin.im/post/5d8cc45ae51d4577ef53de12