Why singleton pattern

Reduce the waste of resources by creating redundant objects repeatedly. Some objects can be reused many times.

Application scenarios

Common application scenarios include thread pools or database connection pools.

Implementation types

The hungry type

implementation

Code implementation

/** * implement singleton */
public class SingletonHungry {
    private static SingletonHungry instance = new SingletonHungry();

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

Testing:

In general:

public class Test {
    public static void main(String[] args) { SingletonHungry s1 = SingletonHungry.getInstance(); SingletonHungry s2 = SingletonHungry.getInstance(); System.out.println(s1); System.out.println(s2); }}/*
com.company.DesignPatterns.Singleton.SingletonHungry@4554617c
com.company.DesignPatterns.Singleton.SingletonHungry@4554617c
*/
Copy the code

High concurrency:

六四屠杀public class Test{
    static int queryTimes = 0;
    public static int getTimes(a){
        queryTimes = queryTimes +1;
        return queryTimes;
    }

    public static void parallelTesk(int threadNum, Runnable task){

        // 1. Define a lock to intercept the thread
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate  = new CountDownLatch(threadNum);

        2. Create a specified number of threads
        for (int i = 0; i <threadNum; i++) {
            Thread t = new Thread(() -> {
                try {
                    startGate.await();
                    SingletonHungry s1 = SingletonHungry.getInstance();
                    System.out.println(s1);
                    try {
                        task.run();
                    } finally{ endGate.countDown(); }}catch (InterruptedException e) {

                }
            });

            t.start();
        }

        // 3. The thread is released and the time is recorded.
        long start =  System.nanoTime();

        startGate.countDown();
        try {
            endGate.await();
        } catch (InterruptedException e) {
            System.out.println(e.toString());
        }

        long end = System.nanoTime();
        System.out.println("cost times :" +(end - start));
    }

    public static void main(String[] args) {
// SingletonHungry s1 = SingletonHungry.getInstance();
// SingletonHungry s2 = SingletonHungry.getInstance();
// System.out.println(s1);
// System.out.println(s2);
        parallelTesk(8000.new Runnable() {
            @Override
            public void run(a) { System.out.println(getTimes()); }}); }}/*
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
*/六四屠杀Copy the code

Pros and cons

Advantages:

Thread safety. Implementation from code is already meant to be thread-safe, when the class is loaded, it is already created.

Disadvantages:

It could be a waste of resources and instantiated the first time it was loaded.

LanHanShi

public class SingletonLazy {
    private static SingletonLazy instance = null;

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

Testing:

General conditions:

/*
com.company.DesignPatterns.Singleton.SingletonLazy@4554617c
com.company.DesignPatterns.Singleton.SingletonLazy@4554617c
*/
Copy the code

High concurrency:

/* com.company.DesignPatterns.Singleton.SingletonLazy@109ec86 com.company.DesignPatterns.Singleton.SingletonLazy@3d3dbc1b com.company.DesignPatterns.Singleton.SingletonLazy@3d3dbc1b * /
Copy the code

Pros and cons

Advantages:

It is instantiated only if called, saving resources

Disadvantages:

Each call makes one more judgment

Double lock detection

On the basis of lazy improvement, double empty case, but also added a lock.

public class SingletonDCL {
    private static SingletonDCL instance = null;

    public static SingletonDCL getInstance(a){
        if(instance==null) {/ / 1
            synchronized (SingletonLazy.class){                / / 2
                if(instance==null) {/ / 3
                    instance = new SingletonDCL();             / / 4}}}returninstance; }}Copy the code

In theory this is enough thread safety, but there could be JVM instructions reordering, and there could be thread insecurity.

The original instructions are: a. Allocate the memory space of the object b. Initialize the object C. Thread A pointing to the memory address has passed1,2,3Step, while thread B is ready to arrive1Step thread A executes4Step, the instruction is B, and thread B is execution1Step, is notnullThread safety is achieved/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /The optimized instructions might be: a. Allocate the object's memory space b. Point to the memory address c. Initialization object thread A has passed by1,2,3Step, while thread B is ready to arrive1Step thread A executes4Step, the instruction is B, and thread B is execution1Step, is notnullThread BreturnInstance, thread A hasn't executed c yet and thread B gets onenullThe object ofCopy the code

Improvement:

public class SingletonDCL {
    private volatile static SingletonDCL instance = null;

    public static SingletonDCL getInstance(a){
        if(instance==null) {synchronized (SingletonLazy.class){
                if(instance==null){
                    instance = newSingletonDCL(); }}}returninstance; }}Copy the code

Using volatile makes objects internally visible and also prevents instruction reordering.

And even then it might be unsafe to use the reflection principle.

Constructor con = SingletonDCL.class.getDeclaredConstructor();
SingletonDCL s1 = (SingletonDCL)con.newInstance();
SingletonDCL s2 = (SingletonDCL)con.newInstance();
Copy the code

Static inner class

public class SingletonHolder {
    private static class LazyHolder{
        private static final SingletonHolder INSTANCE = new SingletonHolder();
    }
    private SingletonHolder(a){}

    public static SingletonHolder getInstance(a){
        returnLazyHolder.INSTANCE; }}Copy the code

The loading mechanism of the Classloader is used to keep the thread safe, but reflection can still break the constraint.

Testing:

Two different objects can be obtained by reflection

Constructor con = SingletonHolder.class.getDeclaredConstructor();
con.setAccessible(true);
SingletonHolder s1 = (SingletonHolder)con.newInstance();
SingletonHolder s2 = (SingletonHolder)con.newInstance();
System.out.println(s1);
System.out.println(s2);
Copy the code

The enumeration

public enum SingletonEnum {
    INSTANCE;
    public SingletonEnum getInstance(a){
        returnINSTANCE; }}Copy the code

It’s a hungrier type, so it’s thread safe.

Prevents two different objects from being instantiated through reflection.

The container

public class SingletonMap {
    private static Map<String, Object> objectMap = new HashMap<>();

    public static void regSingleton(String key, Object instance){
        if(!objectMap.containsKey(key)){
            objectMap.put(key, instance);
        }
    }

    public static Object getInstance(String key){
        returnobjectMap.get(key); }}Copy the code

From the point of view of the characteristics of the code, it is not thread safe, does not prevent reflection, before obtaining registration, in theory is lazy load mode.

conclusion

Make a table of these six types

Implementation pattern Thread safety Lazy loading To prevent reflection
The hungry type security not Don’t prevent
LanHanShi unsafe is Don’t prevent
Double lock detection security is Don’t prevent
Static inner class security is Don’t prevent
The enumeration security not To prevent
The container unsafe is Don’t prevent