“This is the second day of my participation in the August More Text Challenge. For details, see: August More Text Challenge.”

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. ** Singletons are creation patterns. The singleton pattern is widely used in real life. For example: J2EE standard ServletContext, ServletContextConfig and so on.

Two, hungry han type singleton mode

(a), the advantages and disadvantages of hunhan singleton mode

The Hanish singleton pattern is suitable for situations where there are few singleton objects. This ensures absolute thread safety and high execution efficiency.

But the disadvantage of the hungrier singleton pattern is not obvious, that is, all object classes are instantiated when loaded. As a result, if a large number of singletons exist in the system, a large amount of memory is wasted during system initialization. That is, whether objects are used or not, they occupy space and waste memory

(two), hungherian singleton pattern code case

public class HungrySingleton {
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

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

Three, lazy singleton mode

(a), the characteristics of the lazy singleton mode

In order to solve the hungrier memory waste situation mentioned in the second point above, the lazy singleton pattern is proposed here.

A characteristic of the lazy singleton pattern – the singleton is initialized only when it is used.

(two), lazybone singleton pattern code case one

public class LazySimpleSingleton {
    private LazySimpleSingleton(a) {}private static LazySimpleSingleton lazy = null;

    public static LazySimpleSingleton getInstance(a) {
        if (lazy == null) {
            lazy = new LazySimpleSingleton();
        }
        returnlazy; }}Copy the code

(three), lazy singleton pattern code case one shortcomings

Through the code case one way, can successfully solve the hungh-style memory waste problem, but at the same time, it leads to a new problem — multithreaded environment, will appear thread safety problem.

1, simulation thread safety problem results display

(1) Thread safety error

(2) Thread safety without error

2, simulate thread safety issues test code

(1) Thread class: ExcetorThread

public class ExcetorThread implements Runnable {
    @Override
    public void run(a) {
        LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + ":"+ simpleSingleton); }}Copy the code

(2) Test class: LazySimplesingtontest

public class LazySimpleSingletonTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new ExcetorThread());
        Thread t2 = new Thread(new ExcetorThread());
        t1.start();
        t2.start();
        System.out.println("End"); }}Copy the code

3, simulation thread safety problem cause analysis

In this lazy singleton mode, the reason why the thread safety problem occurs is that the following three situations occur when the program is running

1, thread 1 finishes, thread 2 starts — the result is the same singleton (no error)

The running process is displayed in Debug mode

Thread 1 has not created a singleton yet

(2) Thread 2 has not created a singleton yet

(3) Thread 1 creates the singleton object

(4) Thread 1 prints the output

(5) thread 2 determines whether the singleton object exists — it already exists and is no longer created

(6) Thread 1 and thread 2 execute the print operation — the output result is the same singleton object

2. Before thread 1 finishes creating the singleton, thread 2 has judged whether the singleton exists. Thread 2 creates the singleton again, overwriting the singleton created by thread 1 — the result is the same singleton (error)

The running process is displayed in Debug mode

(1) Thread 1 determines whether the singleton object exists, and the statement to create the singleton object has not been executed at this time

(2) Thread 2 also determines whether the singleton exists, and the statement to create the singleton object has not been executed at this time

(3) Thread 1 creates singleton objects

(4) Thread 2 creates a singleton — overrides the singleton created by thread 1

(5) Thread 1 and thread 2 execute the print operation — the output result is the same singleton object

3. Before thread 1 finishes creating the singleton, thread 2 has judged whether the singleton exists. Thread 1 has finished creating and printing before thread 2 creates the singleton. Thread 2 creates again, overwrites the singleton created by thread 1 and finishes printing — the result is a different singleton (error)

The running process is displayed in Debug mode

(1) Thread 1 determines whether the singleton object exists, and the statement to create the singleton object has not been executed at this time

(2) Thread 2 also determines whether the singleton exists, and the statement to create the singleton object has not been executed at this time

(3) Thread 1 creates singleton objects

(4) Thread 1 outputs the print result

(5) Thread 2 creates a singleton — overrides the singleton created by thread 1

(6) Thread 2 performs the print operation — the print results of thread 1 and thread 2 are inconsistent

(four), lazy singleton pattern code case a solution

1, the solution of the code case by locking, making the method to obtain singleton object into thread synchronization

public class LazySimpleSingleton {
    private LazySimpleSingleton(a) {}private static LazySimpleSingleton lazy = null;

    public synchronized static LazySimpleSingleton getInstance(a) {
        if (lazy == null) {
            lazy = new LazySimpleSingleton();
        }
        returnlazy; }}Copy the code

After thread 1 enters the method, thread 2 attempts to enter the method again and then enters the blocked state. Make the thread safety issues mentioned above no longer appear.

(V) Improvement of solutions

1. Code case of improved method

public class LazySimpleSingleton {
    private LazySimpleSingleton(a) {}private static LazySimpleSingleton lazy = null;

    public static LazySimpleSingleton getInstance(a) {
        if (lazy == null) {
            synchronized (LazySimpleSingleton.class) {
                if (lazy == null) {
                    lazy = newLazySimpleSingleton(); }}}returnlazy; }}Copy the code

2. Code analysis

(1) First by placing the synchronized lock in the geiInstance() method

(2), the first singleton object is empty judgment, can make the singleton object already exists no longer in the lock

(3) The second singleton object is empty judgment, avoid two or more than two threads through the first judgment, repeated creation of singleton object, the former created singleton object.

(six), lazybone singleton pattern code case two – static inner class

Code instances of static inner classes

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

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

2. Code analysis

When using LazyInnerClassSingleton, initialize the inner class first by default

(2) If it is not used, the inner class is not loaded

(3) The method of establishing lazy singleton mode with static inner class takes into account the memory waste of hungry singleton mode and synchronized performance problem. Inner classes must be initialized before methods are called, avoiding thread-safety issues.

(vii) The shortcomings and solutions of all the singleton pattern construction methods mentioned above

Reflection breaks singleton test code

public class LazyInnerClassSingletonTest {
    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

2, reflection damage singleton solution

public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton(a) {
        if(LazyHolder.LAZY ! =null) {
            throw new RuntimeException("Not allowed to create multiple instances"); }}public static final LazyInnerClassSingleton getInstance(a) {
        return LazyHolder.LAZY;
    }

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

Four, registered singleton mode

(1), enumerative singleton mode

1, enumeration singleton pattern code case

public enum EnumSingleton {
    INSTANCE;
    private Object data;

    public Object getData(a) {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance(a) {
        returnINSTANCE; }}Copy the code

2, enumerative singleton pattern test code

public class EnumSingletonTest {
    public static void main(String[] args) {
        try {
            EnumSingleton instance1 = null;

            EnumSingleton instance2 = EnumSingleton.getInstance();
            instance2.setData(new Object());

            FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(instance2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("EnumSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            instance1 = (EnumSingleton) ois.readObject();
            ois.close();

            System.out.println(instance1.getData());
            System.out.println(instance2.getData());
            System.out.println(instance1.getData() == instance2.getData());
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

3. Results show

(2) container singleton mode

The container singleton pattern is applicable to scenarios where a large number of singleton objects need to be created for easy management. But it’s not thread-safe.

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

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