Design pattern --- singleton pattern

Keywords: Design Pattern

Application scenario

Only one instance is required. In terms of code and memory, only one instance is required

Key points

First, the construction method is designed to be private

         Unable to new

Second: provide a method to obtain an instance

Related concepts

1.class.forName("") can load the class name. When the static variable is loaded into memory, it will be instantiated

2. The same hash code is not necessarily the same object

3. Variables modified by final   Must initialize

Writing related

Hungry Han style

Advantages: when a class is loaded into memory, it instantiates a simple jvm to ensure thread safety, which is simple and practical

Disadvantages: no matter whether it is used or not, the instantiation is completed when the class is loaded

Code block

public class Single1 {
    private static final Single1 INSTANCE = new Single1();

    private  Single1() {};

    public static Single1 getINSTANCE() {
        return INSTANCE;
    }

    public void m() {System.out.println("m");}

    public static void main(String[] args) {
        Single1 m1 = Single1.getINSTANCE();
        Single1 m2 = Single1.getINSTANCE();
        System.out.println(m1 == m2);
    }
}

Lazy style

Advantages: the purpose of loading on demand is achieved

Disadvantages: it brings the problem of thread insecurity

Code block

public class Single2 {
    //If final is added, it must be initialized
    private static Single2 INSTANCE;

    private Single2() {}

    public static Single2 getINSTANCE() {
        if (INSTANCE == null) {
            try {
                //Sleeping for a millisecond will produce different instantiated instances of the object
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single2();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single2.getINSTANCE().hashCode());
                //Different objects have different hash codes
            }).start();
        }
    }
}

The following code snippets are the advanced version of hungry Chinese style

In addition, synchronized locks the current statement block, but the efficiency is reduced

Code block

public class Single3 {
    //If final is added, it must be initialized
    private static Single3 INSTANCE;

    private Single3() {}

    //Locking every time reduces the efficiency
    public static synchronized Single3 getINSTANCE() {
        if (INSTANCE == null) {
            try {
                //Sleeping for a millisecond will produce different instantiated instances of the object
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single3();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single3.getINSTANCE().hashCode());
                //Different objects have different hash codes
            }).start();
        }
    }
}

The efficiency is improved by reducing the synchronous code block, and the same instance can not be achieved in the case of multiple threads

Code block

public class Single4 {
    //If final is added, it must be initialized
    private static Single4 INSTANCE;

    private Single4() {}

    //Locking every time reduces the efficiency
    public static Single4 getINSTANCE() {
        if (INSTANCE == null) {
            synchronized (Single4.class) {
                try {
                    //Sleeping for a millisecond will produce different instantiated instances of the object
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Single4();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single4.getINSTANCE().hashCode());
                //Different objects have different hash codes
            }).start();
        }
    }
}

Double check single case judgment

Code block

public class Single5 {
    //If final is added, volatile must be initialized to prohibit instruction rearrangement
    private static volatile Single5 INSTANCE;

    private Single5() {}

    //Locking every time reduces the efficiency
    public static Single5 getINSTANCE() {
        if (INSTANCE == null) {
            synchronized (Single5.class) {
                //duplication check
                if (INSTANCE == null) {
                    try {
                        //Sleeping for a millisecond will produce different instantiated instances of the object
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Single5();
                }

            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single5.getINSTANCE().hashCode());
                //Different objects have different hash codes
            }).start();
        }
    }
}

Static inner class mode

Relevant knowledge points:

1. The JVM guarantees that the singleton will not load the internal class when loading the external class, and lazy loading can be realized

2. Privatization. Only internal classes can be new

3. The internal class will not be loaded casually. It will be loaded only when it is called

4. When the virtual machine loads a class, it only loads it once

Code block

public class Single6 {
    private Single6() {}

    private static class Single6Holder {
        private final static Single6 INSTANCE = new Single6();
    }

    public static Single6 getInstance() {
        return Single6Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Single6.getInstance().hashCode());
            }).start();
        }

    }
}

Enumeration class mode

Relevant knowledge points:

1. It can not only solve thread synchronization, but also prevent deserialization

2. The singleton prevents deserialization, reflects the class, and load s it into memory

3. The reason why it will not be deserialized is that the enumeration class has no constructor. Even if it is deserialized, it will only get the value of the enumeration class

4. Grammar is the most perfect

Code block

public enum Single7 {

    INSTANCE;

    public void m() {}

    public static void main(String[] args) {
        for (int i=0;i<100; i++) {
            new Thread(()->{
                System.out.println(Single7.INSTANCE.hashCode());
            }).start();
        }
    }
}

relevant

bean factory in spring framework to guarantee singleton  

Posted by D on Sun, 05 Sep 2021 15:18:13 -0700