Singleton patterns are commonly used in Java, and typically include:
- Lazy singleton pattern
- Hunchman singleton pattern
- Registered singleton pattern (this omission)
1. Characteristics of singleton pattern
1. A singleton can have only one instance object, 2. A singleton class must create its own unique instance, so the constructor is private 3. The singleton class must provide this instance to all other objects. So provide a public method to get an instance of the class externally. The singleton pattern ensures that there is only one instance of a class that is instantiated and made available to the entire system. In computer systems, thread pools, caches, log objects, dialogs, printers, and graphics card driver objects are often designed as singletons. All of these applications function more or less as resource managers. Each computer can have several printers, but only one Printer Spooler, to avoid two print jobs being output to the Printer at the same time. Each computer can have several communication ports, which should be centrally managed by the system to avoid one communication port being invoked by two requests at the same time. In short, the singleton pattern is chosen to avoid inconsistent states and multiple policies. \
Lazy singleton pattern
It is a lazy singleton pattern because its objects are instantiated only when the object is retrieved when a public method is called externally. The lazy singleton pattern: (1) privatize the constructor, (2) declare a private object, and (3) provide a public method for external access to instantiated objects. But the lazy singleton pattern is not thread safe. \
package com.chb.singlePattern; /** lazy singleton: Public class LazySingleton {private LazySingleton() {} private static LazySingleton singleton = null; /** * If multiple threads access this method at the same time, there are thread safety issues. * This is a pitfall of lazy singletons and needs fixing. * @return */ public static LazySingleton getInstance() {if (singleton == null) {// If two threads enter this method at the same time, two objects are created. singleton = new LazySingleton(); } return singleton; }}Copy the code
LazySingleton avoids external instantiation of the class by limiting the constructor to private. Within the same virtual machine scope, the only instance of LazySingleton can only be obtained by the getInstance() method (regardless of reflection). \
Improvement of slacker singleton pattern
Because slacker mode is unsafe in the case of multiple threads, this hidden danger needs to be solved. We can do it in three ways
3.1. Add syncronized to getInstance() for synchronization
/** The lazy singleton pattern is improved by the first method: */ Class LazySingletonSync {private LazySingletonSync() {} private static LazySingletonSync lazySingletonSync = null; public static synchronized LazySingletonSync getInstance() { if (lazySingletonSync == null) { lazySingletonSync = new LazySingletonSync(); } return lazySingletonSync; }}Copy the code
3.2. Double-checked locking
Double-check is not available in J2SE 1.4 or earlier versions due to out-of-order writes for multithreading or JVM tuning. This problem has been fixed in J2SE 5.0, and the volatile keyword can be used to guarantee singletons across multiple threads. Double-checked locking is not thread-safe, and if you want to use it, you need to use the volatile keyword.
Instance = new Instance(). This constructor is A nonatomic operation that generates multiple bytecode instructions. Instance assignment may be performed first, which is actually just a reference to memory that is created for an object, and then instance will not be empty, but the actual initialization has not been performed, and if thread B enters at this point, You will see an Instance object that is not empty but incomplete (not initialized), so you need to add the volatile keyword to prevent instruction reordering optimization and safely implement singletons.
/** The lazy singleton pattern is modified: */ class LazySingletonDoubleSync {private LazySingletonDoubleSync() {} The volatile keyword is required. private static volatile LazySingletonDoubleSync lazySingletonDoubleSync = null; public static synchronized LazySingletonDoubleSync getInstance() { if (lazySingletonDoubleSync == null) { synchronized (LazySingletonDoubleSync.class) { if (lazySingletonDoubleSync == null) { lazySingletonDoubleSync = new LazySingletonDoubleSync(); } } } return lazySingletonDoubleSync; }}Copy the code
3.3. Static inner Classes
This is better than both of the above, because it is thread-safe and avoids the performance impact of synchronization.
Private LazySingletonInner() {} // Public method for external fetching instantiation objects. public static LazySingletonInner getInstance() { return LazyHolder.INSTANCE; } /** * Static inner class: Providing a static final instantiation object * ensures that the object exists when the class is created, and also ensures that the object is secure and unique * since no synchronization is required, Private static class LazyHolder {private static final LazySingletonInner INSTANCE = new LazySingletonInner(); }}Copy the code
4. Hungry singleton mode
The class instantiates the object at the same time as it is created, and uses static final modification to ensure that the instantiated object cannot be changed, ensuring the uniqueness of the object. Also thread safe. Hungry type has been created while class to create a static object for the use of system, won’t change, so naturally is thread-safe. \
package com.chb.singlePattern; Public class HungrySingleton {// Constructor privatized private HungrySingleton(){} public class HungrySingleton() private HungrySingleton(){} Public class HungrySingleton() private HungrySingleton(){} It exists at the same time the class is created. HungrySingleton = new HungrySingleton(); Public static HungrySingleton getInstance() {return HungrySingleton; }}Copy the code
Five, the difference between the three slacker models
The first is to add synchronization to method calls, which is thread-safe, but synchronization every time will affect performance, because synchronization is not required 99% of the time.
The second method, which does two null checks in getInstance, ensures that synchronization is performed only on the first call to the singleton, but it is not thread-safe and uses the volatile keyword. The performance cost of synchronizing every time is avoided.
The third one uses the classloader mechanism to ensure that instance is initialized with only one thread, so it is thread-safe and has no performance loss, so GENERALLY I prefer to use this one.