Java singleton design pattern

Article catalog

1. Introduction to single design mode

The so-called single instance design pattern of a class is to take certain methods to ensure that in the whole software system, there can only be one object instance for a class, and the class only provides a method to obtain its object instance (static method).

For example, Hibernate's SessionFactory acts as a proxy for data storage sources and is responsible for creating Session objects. SessionFactory is not lightweight. Generally, only one SessionFactory is needed for a project. This is a singleton mode.

2. Eight ways of single design pattern

2.1 starved Chinese (static constant)

step

1. Constructor Privatization (prevent new)

2. Create objects inside of class

3. Expose a static public method: getInstance()

code implementation

/**
 * Starved Chinese (static variable)
 */
class Singleton {
    /***
     * 1,Constructor privatization to prevent external new
     */
    private Singleton() {

    }
    /**
     * 2,Create an object instance within this class
     */
    private final static Singleton instance = new Singleton();

    /**
     * 3,Provide a public static method to return the instance object
     */
    public static Singleton getInstance() {
        return instance;
    }
}

public class SingletonTest01 {

    public static void main(String[] args) {
        //test
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

Advantages and disadvantages

1. Advantage: this method is relatively simple, that is, instantiation is completed when the class is loaded. Thread synchronization is avoided.

2. Disadvantage: instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If you never use this instance from the beginning to the end, you will waste memory.

3. This method is based on the classloder mechanism to avoid the problem of multi-threaded synchronization. However, instance is instantiated when the class is loaded. In the singleton mode, most of them call the getInstance method. However, there are many reasons for class loading, so it is uncertain that there are other ways (or other static methods) to cause class loading. In this case, initialize instance It does not achieve the effect of lazy loading.

4. Conclusion: this single instance mode is available, which may cause memory waste.

2.2 starved Chinese (static code block)

code implementation

/**
 * Starved Chinese (static variable)
 */
class Singleton {
    /***
     * 1 Constructor privatization to prevent external new
     */
    private Singleton() {

    }

    private static Singleton instance;
    /**
     * In a static code block, create a singleton object
     */
    static {
        instance=new Singleton();
    }
    /**
     * Provide a public static method to return the instance object
     */
    public static Singleton getInstance() {
        return instance;
    }
}

public class SingletonTest02 {

    public static void main(String[] args) {
        //test
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

Advantages and disadvantages

1. This method is similar to the above method, except that the process of class instantiation is placed in the static code block. When the class is loaded, the code in the static code block is executed and the class instance is initialized. The advantages and disadvantages are the same as above.

2. Conclusion: This singleton mode is available, but it may cause memory waste

2.3 lazy (unsafe thread)

code implementation

class Singleton {
    private static Singleton instance;

    private Singleton() {}

    /**
     * Provide a static public method. When the method is used, create an instance, which is lazy
     */
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

public class SingletonTest03 {

    public static void main(String[] args) {
        System.out.println("Lazy 1, thread unsafe~");
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

Advantages and disadvantages

1. It has the effect of Lazy Loading, but it can only be used in a single thread.

2. If one thread enters the if (singleton == null) judgment statement block and has not yet executed it, another thread also passes the judgment statement, then multiple instances will be generated. Therefore, this method cannot be used in a multithreaded environment.

3. Conclusion: do not use this method in actual development.

2.4 lazy (thread safety, synchronization method)

code implementation

/**
 * Lazy (thread safe, synchronous method)
 */
class Singleton {
	private static Singleton instance;
	
	private Singleton() {}

	/**
	 * Provide a static public method, add the synchronized code to solve the thread safety problem
	 */
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
public class SingletonTest04 {

	public static void main(String[] args) {
		System.out.println("Lazy 2, thread safe~");
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance == instance2); // true
		System.out.println("instance.hashCode=" + instance.hashCode());
		System.out.println("instance2.hashCode=" + instance2.hashCode());
	}

}

Advantages and disadvantages

  1. Solved thread safety problems

  2. The efficiency is too low. When each thread wants to get an instance of the class, it needs to synchronize to execute the getInstance() method. In fact, this method can only execute the instantiation code once. If you want to obtain the instance of this class later, just return. Method synchronization efficiency is too low.

  3. Conclusion: this method is not recommended in actual development.

2.5 lazy (thread safe, synchronized code block)

/**
 * Lazy (thread safe, synchronized code block)
 */
class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;
    }
}

Not recommended, the effect is the same as the previous way

2.6 double inspection

code implementation

/*
 * Lazy (thread safe, synchronous method)
 */
class Singleton {
	private static volatile Singleton instance;
	
	private Singleton() {}

	/**
	 * Provide a static public method, add double check code, solve thread safety problem, and solve lazy loading problem
	 * At the same time, it ensures the efficiency and is recommended for use
	 */
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			synchronized (Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
			
		}
		return instance;
	}
}

Advantages and disadvantages

1. The concept of double check is often used in multi-threaded development. As shown in the code, we have carried out two if (singleton == null) checks, which can ensure thread safety.

2. In this way, the instantiation code is executed only once, and when it is accessed again later, it will judge if (singleton == null), return the instantiated object directly, and avoid repeated method synchronization.

3. Thread safety; delayed loading; high efficiency.

4. Conclusion: in the actual development, this single case design mode is recommended.

2.7 static internal class

code implementation

/**
 * Static internal class completion, recommended
 */
class Singleton {
	private static volatile Singleton instance;
	/**
	 * Constructor privatization
	 */
	private Singleton() {}
	/**
	 * Write a static inner class with a static property Singleton
	 */
	private static class SingletonInstance {
		private static final Singleton INSTANCE = new Singleton(); 
	}
	/**
	 * Provide a static public method and return directly SingletonInstance.INSTANCE
	 * @return
	 */
	public static synchronized Singleton getInstance() {
		return SingletonInstance.INSTANCE;
	}
}

Advantages and disadvantages

1. In this way, the class loading mechanism is adopted to ensure that there is only one thread when initializing an instance.

2. The static internal class mode will not instantiate the Singleton class immediately when it is loaded, but when it needs to be instantiated, the getInstance method will be called to load the SingletonInstance class, thus completing the instantiation of Singleton.

3. The static properties of a class will only be initialized when the class is first loaded, so here, the JVM helps us ensure the thread safety. When the class is initialized, other threads cannot enter.

4. Advantages: avoid thread insecurity, use the characteristics of static internal classes to achieve delayed loading, high efficiency.

5. Conclusion: it is recommended.

2.8 enumeration

code implementation

/**
 * Using enumeration, you can implement a single example, recommended
 */
enum Singleton {
	INSTANCE; //attribute
	public void sayOK() {
		System.out.println("ok~");
	}
}
public class SingletonTest08 {
	public static void main(String[] args) {
		Singleton instance = Singleton.INSTANCE;
		Singleton instance2 = Singleton.INSTANCE;
		
		System.out.println(instance == instance2);
		System.out.println(instance.hashCode());
		System.out.println(instance2.hashCode());
		
		instance.sayOK();
	}
}

Advantages and disadvantages

1. This implements the singleton pattern with the enumeration added in JDK 1.5. It can not only avoid multithreading synchronization, but also prevent deserialization from re creating new objects.

2. This approach is advocated by Josh Bloch, author of Effective Java.

3. Conclusion: it is recommended.

3. Example of using the singleton mode in JDK source code

java.lang.Runtime The first method (static constant) is used

4. Notes and details of single mode

1. The singleton mode ensures that there is only one object in the system memory and saves system resources. For some objects that need to be created and destroyed frequently, the singleton mode can improve system performance.

2. When you want to instantiate a singleton class, you must remember to use the corresponding method to get the object instead of using new.

3. Examples of scenarios: objects that need to be created and destroyed frequently, objects that take too much time or resources (i.e. heavyweight objects) to create, but objects that are often used, tool class objects, objects that frequently access databases or files (such as data sources, session factories, etc.).

Tags: JDK Session Java Hibernate

Posted on Sun, 21 Jun 2020 02:20:19 -0400 by davestevens_uk