Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

preface

Last time we talked about a slightly heavier factory mode, did you digest it and escape from the factory? I don’t mean to say that today’s singleton pattern is just the opposite. It is lonely and seems to be doomed to be single.

Let’s take a look at the use of the singleton in the JDK

The Runtime is used in the JDK

knowledge

There are 8 ways

1) Hungry (static constants) 2) hungry (static code blocks) 3) lazy (thread-unsafe) 4) lazy (thread-safe, synchronized methods) 5) lazy (thread-safe, synchronized code blocks) 6) double check 7) Static inner class 8) enumeration

Hungry (static constant)

Very diligent, creating objects before they are used

1) privatize the constructor (to prevent new) 2) create objects inside the class 3) expose a static public method getInstance() 4) code implementation

public class Singleton1 {

    private  Singleton1(a) {}private final static Singleton1 instance = new Singleton1();

    public static Singleton1 getInstance(a){
        returninstance; }}Copy the code

The advantages and disadvantages

1) Advantages: This method is relatively simple, that is, instantiation is completed at the time of class loading. Thread synchronization issues are avoided. 2) Disadvantages: Instantiation is completed when the class is loaded, which does not achieve the effect of LazyLoading. 3) This approach avoids multithreaded synchronization issues based on the Classloder mechanism. However, instance is instantiated when the class is loaded. In singleton mode, the getInstance method is mostly called. However, there are many reasons for class loading, so it is not certain that there is any other way (or static way) to load a class. In this case, instance loading is not as good as lazyloading

Hungry (static code block)

Advantages and disadvantages are the same as above, can be said to be equivalent to the above ordinary hungry

packageCom.melo.design. singleton pattern. Hungry _ static code block;public class Singleton2 {

    private Singleton2(a) {}private static Singleton2 instance ;
    
    static {
        instance = new Singleton2();
    }

    public static Singleton2 getInstance(a){
        returninstance; }}Copy the code

LanHanShi

Wait until you need it, then create the object

/** * thread safe */
public class Singleton {
    // Private constructor
    private Singleton(a) {}

    // Create an object for the class at the member location
    private static Singleton instance;

    // Provide static methods to get the object
    public static synchronized Singleton getInstance(a) {

        if(instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Lazy (synchronized code block)- cannot be used

In fact, you can’t do thread synchronization, because if you have A thread mess like if A comes in, B comes in, A uses class first, and then new, and THEN B doesn’t check again, and then goes to new again

** Lazy double search (double check)

Just solved the lazy synchronized code block problem above, one more step if judgment

  • Note that double search means double check, not double lock!!!!
packageCom.melo.design. singleton pattern. Double retrieval;public class Singleton {
    
    // Note the volatile modifier
    private volatile static Singleton singleton;
    
    private Singleton (a){}public static Singleton getSingleton(a) {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = newSingleton(); }}}returnsingleton; }}Copy the code

** Note: volatile

In multithreaded situations, null-pointer problems can occur because the JVM performs optimization and instruction reordering operations when instantiating objects. To solve the problem of null-pointer exceptions caused by double-checked locking, use only the volatile keyword, which guarantees visibility and order.

  • So maybe there’s some reordering going on here and there, and since there are multiple instructions that can be reordered, that means that herenew Singleton() operations are not atomic
    1. Allocate memory for singleton first
    2. Call the constructor to initialize the member variable Singleton
    3. Let the singleton reference point to the allocated memory space. = null)
  • So when does reordering of instructions cause problems?
    • For example, in the case of c-a-B, c goes first, and another thread calls getSingleton. Note that the thread will return instance if singleton is not null. However, the resulting singleton is not available because the object has not been initialized and the state is still not available, which will cause an exception to occur.
  • Therefore, the variable must be decorated with the volatile keyword to avoid instruction reordering

The knowledge about volatile is still being sorted out. Hopefully, the knowledge about concurrency and locking will be sorted out soon

* static inner class

The final static constant is returned directly from the inner static class.

public class Singleton {  
    
    // Static inner class that defines a final static constant
    private static class SingletonHolder {  
    	private static final Singleton INSTANCE = new Singleton();  
    }  
    
    private Singleton (a){}  
    
    public static final Singleton getInstance(a) {  
    	returnSingletonHolder.INSTANCE; }}Copy the code

1) This approach uses a classloading mechanism to ensure that only one thread is used to initialize instances. 2) The static inner class mode will not be instantiated immediately when the Singleton class is loaded. Instead, the SingletonInstance class will be loaded only when the Singleton class needs to be instantiated by calling the getInstance method. 3) Static properties of a class are initialized only when the class is first loaded, so the JVM helps us keep our threads safe from entering the class while it is initialized. 4) Advantages: Avoid thread insecurity, use static internal class characteristics to achieve delayed loading, high efficiency 5) Conclusion: Recommended.

** enumeration (anti-reflection and anti-ordering)

  • Interfaces can be inherited or implemented (seemingly like a normal class)
  • Use the direct stuuserService.instance. method instead
public enum StuUserService implements StuUserServiceInter {
    /** * The only instance of this class */
    INSTANCE;

    @Override
    public void test1(a){
        System.out.println("111");
    }

    public static void main(String[] args) { StuUserService.INSTANCE.test1(); }}Copy the code

Expand the knowledge

First, enumerations are like classes. An enumeration can have member variables, member methods, and constructors.

Each enumerator is considered an object of the class and member variables can be set. Member methods can then be set based on the member variables

Reflection and serialization break singletons

reflection

Privatized constructors are not safe. It can’t resist reflex attacks!!

Destroy the proud double search.

packageCom.melo.mydesign. singleton pattern. Double retrieval;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton (a){}

    public static Singleton getSingleton(a) {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = newSingleton(); }}}return singleton;
    }
    // Start something
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // Object 1 generated by calling the singleton method
        Singleton singleton1 = Singleton.getSingleton();
        // Get all constructors (including private ones)
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        // Set accessibility
        constructor.setAccessible(true);
        // Constructor generation
        Singleton singleton2 = constructor.newInstance();
        // Check whether it is equal, find false, already broken singletonSystem.out.println(singleton1==singleton2); }}Copy the code

serialization

Also break double search

public static void main(String[] args) throws Exception {
        Singleton s = Singleton.getInstance();

        byte[] serialize = SerializationUtils.serialize(s);
        Object deserialize = SerializationUtils.deserialize(serialize);

        System.out.println(s);
        System.out.println(deserialize);
        System.out.println(s == deserialize);

    }
Copy the code

Let’s look at enumeration

Reflection reports an error as soon as it runs

Exception in thread "main" java.lang.NoSuchMethodException: com.fsx.bean.EnumSingleton.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at com.fsx.maintest.Main.main(Main.java:19)
Copy the code

The main thing is that there are enumerations in the source code, and if the class is an enumeration modifier, an exception is thrown

if((clazz.getModifiers() & Modifier.ENUM) ! =0) {throw new IllegalArgumentException("Cannot reflectively create enum objects");
}
Copy the code

Serialization is also defensible

packageCom.melo.mydesign. singleton pattern. Enumeration;importCom.melo.mydesign. singleton pattern. Double retrieval. The Singleton;import org.springframework.util.SerializationUtils;

public enum StuUserService implements StuUserServiceInter {
    /** * The only instance of this class */
    INSTANCE;

    @Override
    public void test1(a){
        System.out.println("111");
    }

    public static void main(String[] args) {
// StuUserService.INSTANCE.test1();

        StuUserService s = StuUserService.INSTANCE;

        byte[] serialize = SerializationUtils.serialize(s); Object deserialize = SerializationUtils.deserialize(serialize); System.out.println(s); System.out.println(deserialize); System.out.println(s == deserialize); }}Copy the code

conclusion

  • There are two ways to access a class object that has a static variable instance and a private constructor

Lazy: create an object when it is used (then lock it). Lazy: create an object when it is used (then lock it), which ensures thread-safe but wastes memory