Learning singleton pattern (a)

What is a singleton? Why use a singleton?

A class is designed, on behalf of which represents a certain behavior (method), attributes (member variables), but in general, when we want to use this class, use the new keyword, this time will help us construct jvm an instance of the class. And we know that for new keyword and that instance, by comparison, it is relatively resource-intensive. So if we want to be able to start in the way when the JVM new good, or in some instances a new well in the future, will no longer need this kind of action, you can save a lot of resources.

What classes can use a singleton?

In general, we always want to stateless class can be designed as a single example, that the stateless represent? In simple terms, for the same example, if multiple threads simultaneously, and does not use additional means of thread synchronization, thread synchronization problem does not occur, we can be considered stateless , and then a simple point: is not a class member variable, or its member variables are stateless , we can consider the design as a single case.

Implementation

Well, we already know what is a singleton, how to achieve a single case under Why use singleton, then we are going to continue the discussion.
In general, we can put into a single case of a single case of behavioral and single cases of upper management . Singleton behavior on behalf regardless of the operation (here aside cloneable, reflection), jvm instance start to finish in only one category, and single cases of upper management can be understood as: no matter who is going to use this class, must observe certain rules , for example, we use a class, only 'pick up' from a designated place, so get is the same class.
For singleton on the management , I believe that we are most familiar with is the spring, spring all the classes into a container , the future use of the class are from the container pick up, thus ensuring a single case.
So here is the rest of us then talk about how to achieve a single case on the behavior of the. In general, to achieve this single embodiment, there are two ideas, private constructor, enumeration .

Enumeration achieve a singleton

Enumeration embodiment is a method for single most preferred, because even serialization, there is no way to destroy the reflection of a single embodiment, examples:

public enum SingletonEnum {
    INSTANCE;

    public static void main(String[] args) {
        System.out.println(SingletonEnum.INSTANCE == SingletonEnum.INSTANCE);
    }
}

It was natural that to true , and if we try to use a single case of damage reflection:

public enum BadSingletonEnum {
    /**
     *
     */
    INSTANCE;

    public static void main(String[] args) throws Exception{
        System.out.println(BadSingletonEnum.INSTANCE == BadSingletonEnum.INSTANCE);

        Constructor<BadSingletonEnum> badSingletonEnumConstructor = BadSingletonEnum.class.getDeclaredConstructor();
        badSingletonEnumConstructor.setAccessible(true);
        BadSingletonEnum badSingletonEnum = badSingletonEnumConstructor.newInstance();

        System.out.println(BadSingletonEnum.INSTANCE == badSingletonEnum);
    }
}

The results are as follows:

Exception in thread "main" java.lang.NoSuchMethodException: cn.jsbintask.BadSingletonEnum.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at cn.jsbintask.BadSingletonEnum.main(BadSingletonEnum.java:18)

Abnormal actually no init method, which is why? That decompile bytecode we see this enumeration class:

// class version 52.0 (52)
// access flags 0x4031
// signature Ljava/lang/Enum<Lcn/jsbintask/BadSingletonEnum;>;
// declaration: cn/jsbintask/BadSingletonEnum extends java.lang.Enum<cn.jsbintask.BadSingletonEnum>
public final enum cn/jsbintask/BadSingletonEnum extends java/lang/Enum {

  // compiled from: BadSingletonEnum.java

  // access flags 0x4019
  public final static enum Lcn/jsbintask/BadSingletonEnum; INSTANCE

  // access flags 0x101A
  private final static synthetic [Lcn/jsbintask/BadSingletonEnum; $VALUES
}

It was found that this enumeration class inherits from the abstract class java.lang.Enum , we then look at the Enum , we found constructor:

/**
    * Sole constructor.  Programmers cannot invoke this constructor.
    * It is for use by code emitted by the compiler in response to
    * enum type declarations.
    *
    * @param name - The name of this enum constant, which is the identifier
    *               used to declare it.
    * @param ordinal - The ordinal of this enumeration constant (its position
    *         in the enum declaration, where the initial constant is assigned
    *         an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

Then we then change the code, reflecting call this constructor:

public enum BadSingletonEnum {
    /**
     *
     */
    INSTANCE();

    public static void main(String[] args) throws Exception{
        System.out.println(BadSingletonEnum.INSTANCE == BadSingletonEnum.INSTANCE);

        Constructor<BadSingletonEnum> badSingletonEnumConstructor = BadSingletonEnum.class.getDeclaredConstructor(String.class, int.class);
        badSingletonEnumConstructor.setAccessible(true);
        BadSingletonEnum badSingletonEnum = badSingletonEnumConstructor.newInstance("test", 0);

        System.out.println(BadSingletonEnum.INSTANCE == badSingletonEnum);
    }
}

The results are as follows:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at cn.jsbintask.BadSingletonEnum.main(BadSingletonEnum.java:21)

Although this method is found, but directly to us something of Can not reflectively the Create Objects enum , enumeration object can not reflect the creation, then we continue to look newInstance (...) this method:

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }
The key code is: IF (! (Clazz.getModifiers () & Modifier.ENUM) = 0) the throw new new IllegalArgumentException ( "enum of Can not reflectively the Create Objects"); , it is jdk rejected the use of reflection fundamental to create (known as java enum what is recommended for single embodiment of the bar), in addition, we observe again under the enum clone and sequence-based methods, as follows:


protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

private void readObject(ObjectInputStream in) throws IOException,
    ClassNotFoundException {
    throw new InvalidObjectException("can't deserialize enum");
}

private void readObjectNoData() throws ObjectStreamException {
    throw new InvalidObjectException("can't deserialize enum");
}

At a glance, direct throw an exception, does not allow it! (True son of the pro-series) .
So, the conclusion is: the enumeration is the most likely way to achieve a single case!

Private constructor

Another embodiment of the single most common method is implemented private constructor, OA instance Public method , although this method can still clone, sequence of, damage for a single exemplary embodiment (except in special circumstances, we will not do), but it is the most easily understood use. And this way is divided into a full Chinese type , a hungry man style .

Hungry Chinese-style

See the name to know, hungry! (Ahem, a joke), when it refers to a jvm loaded class is instantiated, so that multiple threads can solve the synchronization problem fundamentally, the following examples:


public class FullSingleton {
    private static FullSingleton ourInstance = new FullSingleton();

    public static FullSingleton getInstance() {
        return ourInstance;
    }

    private FullSingleton() {
    }

    public static void main(String[] args) {
        System.out.println(FullSingleton.getInstance() == FullSingleton.getInstance());
    }
}

It was natural that to true , although this approach is very handy to help us solve the problem of multi-threaded example, but the disadvantages are also obvious, because this code private static FullSingleton ourInstance = new FullSingleton ( ); relationship, so once the class jvm will be loaded immediately instantiated, that if we do not want to use this class how to do it? It is not wasted it? In this case, we look at the alternatives! Full of Chinese style.

Full of Chinese style

Since it is full , it means that it is not in a hurry, then we can write:

public class HungryUnsafeSingleton {
    private static HungryUnsafeSingleton instance;
    
    public static HungryUnsafeSingleton getInstance() {
        if (instance == null) {
            instance = new HungryUnsafeSingleton();
        }
        
        return instance;
    }
    
    private HungryUnsafeSingleton() {}
}

The intention is easy to understand, is to use the getInstance () method before going to check instance, if it is null, then a new, so afraid to waste, but this time the question came: there is now such a situation, there are two in simultaneous threads to instane == null this statement, and pass, then they will have to instantiate an object, so that you are not a singleton. If so, can there be any solution? Lock method

  1. Direct synchronization method
    this method is more crisp, which is locked directly on the getInstance () method, this would resolve the problem of threads:

public class HungrySafeSingleton {
    private static HungrySafeSingleton instance;

    public static synchronized HungrySafeSingleton getInstance() {
        if (instance == null) {
            instance = new HungrySafeSingleton();
        }

        return instance;
    }

    private HungrySafeSingleton() {
        System.out.println("HungryUnsafeSingleton.HungryUnsafeSingleton");
    }

    public static void main(String[] args) {
        System.out.println(HungrySafeSingleton.getInstance() == HungrySafeSingleton.getInstance());
    }
}

Very simple, easy to understand, lock, only one thread can instance of the object. However, this time the problem again, we know for static methods, synchronized keyword lock the entire Class, this time they have performance problems (Nima ink), then there is no way to optimize it? Double checking locks :

public class HungrySafeSingleton {
    private static volatile HungrySafeSingleton instance;

    public static HungrySafeSingleton getInstance() {
        /* 使用一个本地变量可以提高性能 */
        HungrySafeSingleton result = instance;

        if (result == null) {

            synchronized (HungrySafeSingleton.class) {

                result = instance;
                if (result == null) {
                    instance = result = new HungrySafeSingleton();
                }
            }
        }

        return result;
    }

    private HungrySafeSingleton() {
        System.out.println("HungryUnsafeSingleton.HungryUnsafeSingleton");
    }

    public static void main(String[] args) {
        System.out.println(HungrySafeSingleton.getInstance() == HungrySafeSingleton.getInstance());
    }
}

Intention is also evident, synchronized keyword only increase in the key areas, and improve the performance (effective java) by a local variable, so thread-safe and does not waste resources of a single case is complete.

to sum up

In this chapter we step by step from what was a single case, to why to use a single case, and then how to use a singleton, and analyze why the enumeration is the most appropriate way to achieve from the source point of view, then went on to explain the formula-fed, hungry Han writing style and the benefits and disadvantages.
Examples Source: https://github.com/jsbintask22/design-pattern-learning.git
article Original Address: https://jsbintask.cn/2019/01/29/designpattern/singleton/ , please indicate the source.

Guess you like

Origin www.cnblogs.com/wcgstudy/p/11408495.html