Hello guys, MY name is Jack Xu, and today I’m going to talk to you about a commonplace topic. Singletons are one of the most commonly used design patterns. Anyone familiar with design patterns will know singletons. Online articles are also a lot of, but uneven, good and bad, or say not to the point, or write incomplete, I tried to write oneThe most complete singleton pattern in historyOne article is enough for you to read.

Definition and application scenario of singleton pattern

The singleton pattern ensures that there is absolutely only one instance of a class in any case and provides a global point of access. The singleton pattern is the creation pattern. Many times the entire system only needs to have one global object, which helps us coordinate the overall behavior of the system. For example, in a server program, the configuration information of the server is stored in a file, the configuration data is read by a singleton, and then other objects in the service process through the singleton to obtain the configuration information. This approach simplifies configuration management in complex environments. The idea behind the singleton is to hide all of its constructors and provide a global access point.

The hungry type

This is easy, all the guys have written, this is initialized as soon as the class loads, because it’s hungry, it creates an object for you to start with, this is absolutely thread-safe, it’s instantiated before the thread even exists, there’s no access security issue. The disadvantage is that if I don’t use it, IF I don’t use it, I’m taking up space and wasting memory.

public class HungrySingleton {

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(a) {}public static HungrySingleton getInstance(a) {
        returnhungrySingleton; }}Copy the code

There is also a hunhun-style variant, static code block writing method, the principle is the same, as long as it is static, when the class has been successfully initialized, this and the above than no difference, nothing more than a b, looks like the above kind of hanging, because not many people have seen.

public class HungryStaticSingleton {

    private static final HungryStaticSingleton hungrySingleton;

    static {
        hungrySingleton = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(a) {}public static HungryStaticSingleton getInstance(a) {
        returnhungrySingleton; }}Copy the code

LanHanShi

Simple slob

In order to solve the hungry of the manger, leads to the following this simple LanHanShi writing, I start with an object at the beginning, but does not create his first, when used to judge whether is empty, if is empty I can create an object to return to, if not null is returned directly, why call LanHanShi, is because he is very lazy, It looks ok to create it when you need it, but it can cause thread safety problems in multithreaded situations.

public class LazySimpleSingleton {

    private static LazySimpleSingleton instance;

    private LazySimpleSingleton(a) {}public static LazySimpleSingleton getInstance(a) {
        if (instance == null) {
            instance = new LazySimpleSingleton();
        }
        returninstance; }}Copy the code

If (instance==null) if (instance==null) if (instance==null) if (instance==null) if (instance==null) if (instance==null) if (instance==null) if (instance==null) if (instance==null) As shown in the figure below.How do you solve this problem by simply adding synchronized, and thus maintaining thread safety

Double check lock (DCL)

Written above such a disadvantage, performance is low, only when first initialize the need for concurrency control, and later do not need to control the incoming requests, synchronized to add on the way now, I don’t tube you generate into generated, as long as to the queue, will have to give me so this kind of performance is extremely low, that how to do? Synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized = synchronized (PS: For those who want to learn more about synchronized, see my other article.)

We look at line 19 and see that the synchronized package is on the block of code. When Singleton == null, we only lock the logic that creates the object. If Singleton! = null, return directly, greatly improved efficiency. Singleton == null at line 21, and synchronized at line 18. Singleton == null Walks down by the first thread to create the object, the second thread waits for the first thread execution to end, I also go down, and then create an object, it still can’t control the singleton, so a second thread in 21 trade when go down in the judgment, whether by another thread has been created, this is to double check the lock, conducted two non-empty judgment.

We see the volatile keyword in line 11, which is used to prevent reordering of instructions. When we create the object, we will go through the following steps, but these steps are not atomic. Computers are clever, and sometimes they do not execute in order 1234, but 3214, to improve efficiency. If the first thread executes instance = new LazyDoubleCheckSingleton(), then the second thread returns the object as a non-null object. If the first thread executes instance = new LazyDoubleCheckSingleton(), then the second thread returns the object as a non-null object. But the object hasn’t been initialized yet, so the second thread gets an uninitialized object! That’s why we use volatile.

Initialize the object. 3. Set instance to point to the newly allocated memory address 4. First access objectCopy the code

Finally,, double check locks, while improving performance, are not elegant enough in my opinion, and can be tricky, especially for novices, who don’t understand why they are written this way.

Static inner class

Above is to reduce the lock granularity when creating objects, but no matter on methods or add on the block of code, is used to lock, just use the lock will cause performance problems, it did not lock the way, the answer is yes, that is the way of the static inner class, he is a feature of using the Java code, Static inner classes are not loaded when the main class is loaded. They are only loaded and initialized when the getInstance() method is called

/ * * *@authorJack Xu * takes care of hungrier memory waste as well as synchronized performance issues */
public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton(a) {}public static final LazyInnerClassSingleton getInstance(a) {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        private static final LazyInnerClassSingleton INSTANCE = newLazyInnerClassSingleton(); }}Copy the code

Well, at this point I have introduced the five kinds of single case writing, through the evolution of reasoning, to the fifth time already is the perfect way, both the hungry type of wasted memory, give attention to two or morethings also synchronized performance issues, that he really must perfect, actually otherwise, he had a security issue, then we speak in order of destruction, There are two ways to reflect and serialize.

The destruction of singletons

reflection

We know that in the singleton above, we put the private keyword on the constructor, so that we don’t want the outside to create the object in a new way, but there’s a violent way, and I’m not going to do that, you don’t want me to create a new, so I’m going to reflect it to you, and here’s the code

/ * * *@author jack xu
 */
public class ReflectDestroyTest {

    public static void main(String[] args) {
        try{ Class<? > clazz = LazyInnerClassSingleton.class; Constructor c = clazz.getDeclaredConstructor(null);
            c.setAccessible(true);
            Object o1 = c.newInstance();
            Object o2 = c.newInstance();
            System.out.println(o1 == o2);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

C. setaccessible (true) == == == == == == == == == The final result is false. Indeed, two objects were created and the reflection destruction singleton succeeded.

So how do you prevent reflection, simply by adding a judgment to the constructor

public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton(a) {
        if(LazyHolder.INSTANCE ! =null) {
            throw new RuntimeException("Don't try to break a singleton pattern with reflection."); }}public static final LazyInnerClassSingleton getInstance(a) {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        private static final LazyInnerClassSingleton INSTANCE = newLazyInnerClassSingleton(); }}Copy the code

When looking at the results to prevent reflection success, when the constructor is called and the singleton instance object is found to be no longer empty, throw an exception that stops you from creating any more.

serialization

The following is another example of singleton failure: implement the Serializable interface on a static inner class, then write a test method, create an object, serialize it, deserialize it, and compare

    public static void main(String[] args) {

        LazyInnerClassSingleton s1 = null;
        LazyInnerClassSingleton s2 = LazyInnerClassSingleton.getInstance();

        FileOutputStream fos = null;
        try {

            fos = new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (LazyInnerClassSingleton) ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);

        } catch(Exception e) { e.printStackTrace(); }}Copy the code

When you deserialize an object, the object generated by ObjectInputStream’s readObject actually creates a new object by calling the no-parameter constructor in reflection mode, so the deserialized object is not consistent with the manually created object.So how do you avoid this, again, by simply adding a readResolve method to the static inner class

    private Object readResolve(a) {
        return LazyHolder.INSTANCE;
    }
Copy the code

If you’re looking at the readObject() method of ObjectInputStream, you’ll see that it eventually calls the readResolve() method.So far, the history of the most cattle B singleton generation, has been impeccable, impeccable.

The enumeration

So why would I enumerated in the introduction here, in the Effective Java, a way of enumeration is recommended, because he is simple enough, thread safe, will not be reflected and serialization, everybody look at just a few words, not like the one above has achieved the best b method and although very annoying, but the process Performance, memory, thread safety, corruption, add code here and there to get the final result. While using enumerations, interested partners can decompile look at the bottom of the enumeration is actually a class class, and we consider these problems JDK source code actually help us have been implemented, so in the Java level we only need to use three sentences can fix!

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod(a) {}}Copy the code

So far, I through the layers of evolution, from the shallow to the deep to introduce you so many singletons writing, not perfect to perfect, so much is also very common online writing, below I send you two eggs, expand the other way to write singletons.

eggs

Container singleton

Container s singleton is our mode of managing singleton in Spring. We usually create a lot of beans in the project. When the project starts, Spring will manage them for us and help us to load them into the container.

public class ContainerSingleton {
    private ContainerSingleton(a) {}private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className) {
        Object instance = null;
        if(! ioc.containsKey(className)) {try {
                instance = Class.forName(className).newInstance();
                ioc.put(className, instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return instance;
        } else {
            returnioc.get(className); }}}Copy the code

If an object does not exist, create an object using reflection, save it to the map, and then return it to the map. Let’s test this by creating a Pojo object, then pulling it out of the container twice, comparing it to true, and proving that it is the same object.But there is a problem, this way of writing is not thread safe, so how to do it, I leave it to my friends to think about it.

CAS singleton

Start with an interview question: How do you implement a thread-safe singleton without using synchronized and lock? Synchronized: Synchronized: synchronized: synchronized: synchronized: synchronized: synchronized: synchronized: synchronized: synchronized That is, using the thread-safe mechanism of ClassLoader.

The thread-safe mechanism of a ClassLoader is that its loadClass method uses the synchronized keyword when loading a class. Because of this, unless overridden, this method is synchronized throughout the load by default, which makes it thread-safe.

So what is the answer, is to use CAS optimistic locking, although he has a lock words in the name, but in fact is no lock technology, when multiple threads try to use the CAS for the same variable, at the same time only one thread can update variable values, and all the other thread failure, failure of thread will not be suspended, but was told that failed in this contest, And you can try again with the following code:

/ * * *@author jack xu
 */
public class CASSingleton {
    private static final AtomicReference<CASSingleton> INSTANCE = new AtomicReference<CASSingleton>();

    private CASSingleton(a) {}public static CASSingleton getInstance(a) {
        for(; ;) { CASSingleton singleton = INSTANCE.get();if (null! = singleton) {return singleton;
            }

            singleton = new CASSingleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                returnsingleton; }}}}Copy the code

The new JUC package in JDK1.5 is based on CAS, which is a common implementation of non-blocking algorithms such as synchronized. It is a busy-wait algorithm that relies on the underlying hardware implementation and does not have the additional costs of thread switching and blocking compared to locking. Can support a large degree of parallelism. Although CAS does not use locks, it is constantly spinning, which causes a large execution overhead on the CPU. We do not recommend using it in production, so why do I mention it? Because it is typical of working screws, interviewing and building rockets! You u don’t have to, but you have to know, right?

Finally, the original is not easy, if you think the writing is good, please click like!