Personal Technology Blog (IBLi)

CSDN making the nuggets

The singleton pattern

concept

Singleton pattern: A class has absolutely one instance in any case and provides a global access point (getInstance method). The implementation is basically to hide its constructor, and the singleton pattern is a creation pattern. Some practical application scenarios such as DBpool, ServletContext, ServletConfig, etc

Write in singleton mode

Hangry singleton

Create an instance when the singleton class is first loaded;

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

    private HungrySingleton(a){}

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

advantages

1, high execution efficiency, without any lock

disadvantages

The class is initialized as soon as it is loaded. In some cases, this may cause memory waste. If there is a large number of classes, many classes will be initialized, occupying a large amount of memory;

limitations

Spring can’t be used, and when Spring starts, there’s a lot of class loading.

The second way to write hungry

public class HungryStaticSingleton {

    private static final HungryStaticSingleton hungrySingleton ;

    static {
        hungrySingleton = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(a){}

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

The difference is simply the order in which the classes are loaded.

Lazy singleton

An instance is created when it is called by an external class;

public class LazySingleton {

    private static LazySingleton instance = null;

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

advantages

1, save memory

disadvantages

1. The real singleton cannot be guaranteed and the thread is not safe

Threads are unsafe for two reasons

  • Subsequent threads overwrite instances created by previous threads
  • At the same time, enter the judgment condition, return in order, when there is no overwrite, return the instance

Solutions:

public static synchorized LazySingleton getInstance() {
    if (instance == null) {
        instance = new LazySingleton();
    }
    return instance;
}
Copy the code

However, when the getInstance method adds a lock, performance degrades. If there are many requests, all threads except the one that acquired the lock will have to wait.

How to optimize?

public class LazyDclSingleton {

    private static LazyDclSingleton instance = null;

    private LazyDclSingleton(a) {}// The following thread overwrites the instance created by the previous thread
    public static LazyDclSingleton getInstance(a) {
        if (instance == null) {
            synchronized (LazyDclSingleton.class) {
                instance = newLazyDclSingleton(); }}returninstance; }}Copy the code

Double check lock

public class LazyDclSingleton {

    private static volatile LazyDclSingleton instance = null;

    private LazyDclSingleton(a) {}public static LazyDclSingleton getInstance(a) {
        // Check whether to block
        if (instance == null) {
            synchronized (LazyDclSingleton.class) {
                // Check whether you want to create an instance
                if (instance == null) {
                    instance = newLazyDclSingleton(); }}}returninstance; }}Copy the code

limitations

Private static volatile LazyDclSingleton instance = null; (Volatile disallows instruction reordering)

Double-check the advantages and disadvantages of locks

advantages

1, high performance, can ensure thread safety

disadvantages

1, code readability check, not beautiful enough, code is not elegant enough

Static inner class writing

/** * Static inner classes * Static inner classes are only built when used * classPath:.. /LazyInnerClassSingleton.class * .. /LazyInnerClassSingleton$lazyholder.class * * Advantages: Elegant writing, using Java language syntax, high performance, avoid memory waste * * Disadvantages: can be broken by reflection */
public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton(a){}
    public static LazyInnerClassSingleton getInstance(a){
        return LazyHolder.instance;
    }

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

advantages

1, elegant writing, the use of Java language syntax 2, high performance 3, avoid memory waste

disadvantages

1. Singletons can be destroyed by reflection

/** * Reflection breaks singleton mode */
public class ReflectTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<? > clazz = LazyInnerClassSingleton.class; Constructor<? > declaredConstructor = clazz.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true); Object instance = declaredConstructor.newInstance(); System.err.println(instance); }}/ / print result com. Ibli. JavaBase. The pattern. The singleton. LazyInnerClassSingleton @ 38 af3868
Copy the code

Workaround: Add a judgment in the constructor to throw an exception to terminate the creation if the instance has already been created;

 private LazyInnerClassSingleton(){
        if (LazyHolder.instance != null){
            throw new IllegalArgumentException();
        }
    }
Copy the code

Registered singleton

Each instance is cached in a container, and the instance is retrieved using a unique flag

The enumeration of writing

public enum  EnumSingleton  {

    INSTANCE;

    private Object object;

    public Object getObject(a) {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

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

Use and test

public class EnumSingletonTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingleton enumSingleton =   EnumSingleton.getInstance();
        enumSingleton.setObject(new Object());

        // Try to use reflection destructionClass<? > clazz = EnumSingleton.class; Constructor<? > declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
        System.err.println(declaredConstructor);
        declaredConstructor.setAccessible(true); Object object = declaredConstructor.newInstance(); System.err.println(object); }}Copy the code

Test results:

private com.ibli.javaBase.pattern.singleton.EnumSingleton(java.lang.String,int)
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.ibli.javaBase.pattern.singleton.EnumSingletonTest.main(EnumSingletonTest.java:17)
Copy the code

The reason is limited in the underlying JDK source code

@CallerSensitive 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); }} // Can't create enumeration type 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; }Copy the code

Advantages and disadvantages of enumeration

advantages

1, elegant writing, easy to use

disadvantages

2. As with hungry, it can cause a lot of memory waste in some cases

Container singletons

public class ContainerSingleton {

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

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

The test class:

public class ContainerSingletonTest {
    public static void main(String[] args) {
        Object o1 = ContainerSingleton.getInstance("com.ibli.javaBase.pattern.singleton.Pojo");
        Object o2 = ContainerSingleton.getInstance("com.ibli.javaBase.pattern.singleton.Pojo");
        System.err.println(o1 == o2); // true}}Copy the code

Container singleton writing is suitable for scenarios where a large number of singleton instances are created, similar to Spring’s IOC container. Of course, there is a thread safety issue with the above method; // TODO

Serialization breaks the singleton pattern

/** * serialization: Convert the state of an object in memory to bytecode and then write the bytecode to disk as an IO output stream * deserialization: Read the persistent bytecode content into memory as an IO stream and then convert it into a Java object */
public class SerializableSingleton implements Serializable {

    private static final SerializableSingleton serializableSingleton = new SerializableSingleton();

    private SerializableSingleton(a) {}public static SerializableSingleton getInstance(a) {
        returnserializableSingleton; }}Copy the code

The test class:

public class SerializableSingletonTest {
    public static void main(String[] args) {
        SerializableSingleton s1;
        SerializableSingleton s2 = SerializableSingleton.getInstance();

        FileOutputStream fos;
        try {
            fos = new FileOutputStream("SerializableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();


            FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SerializableSingleton) ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Results:

com.ibli.javaBase.pattern.singleton.SerializableSingleton@20ad9418
com.ibli.javaBase.pattern.singleton.SerializableSingleton@681a9515
false
Copy the code

Workaround: Add a method in SerializableSingleton

Private Object readResolve() {return serializableSingleton; }Copy the code

Results:

com.ibli.javaBase.pattern.singleton.SerializableSingleton@681a9515
com.ibli.javaBase.pattern.singleton.SerializableSingleton@681a9515
true
Copy the code

Reason: ois readObject (); The readResolve method is checked underneath, and if it does not exist, a new instance is generated using reflection;

ThreadLocal singleton

A relatively rare singleton pattern is described below

public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingleton =
            new ThreadLocal<ThreadLocalSingleton>() {
                @Override
                protected ThreadLocalSingleton initialValue(a) {
                    return newThreadLocalSingleton(); }};private ThreadLocalSingleton(a){}

    public static ThreadLocalSingleton getInstance(a){
        returnthreadLocalSingleton.get(); }}Copy the code
public class ThreadLocalExector implements Runnable{
    @Override
    public void run(a) { System.err.println(ThreadLocalSingleton.getInstance()); }}Copy the code

Testing:

public class ThreadLocalSingletonTest {

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

        Thread thread1 = new Thread(new ThreadLocalExector());
        Thread thread2 = new Thread(newThreadLocalExector()); thread1.start(); thread2.start();; }}Copy the code

Results:

com.ibli.javaBase.pattern.singleton.ThreadLocalSingleton@38af3868  1
com.ibli.javaBase.pattern.singleton.ThreadLocalSingleton@38af3868  2

com.ibli.javaBase.pattern.singleton.ThreadLocalSingleton@10c69a60  3
com.ibli.javaBase.pattern.singleton.ThreadLocalSingleton@2f1eeb2f  4
Copy the code

Although the results of the above 3 and 4 are different, they actually achieve the effect of [singleton].

It’s too crowded at the bottom we’ll meet you higher.