Singleton pattern is the most commonly used pattern in design patterns. It belongs to object creation mode, which can ensure that only one instance of a class is generated in the system. Such behavior can bring two benefits:
- For frequently used objects, the time spent creating objects can be omitted, which is a very considerable overhead for those heavyweight objects.
- Since the number of new operations is reduced, the frequency of system memory usage will also be reduced, which will reduce GC pressure and shorten GC pause time.
In practical applications, there are many times when we only need one object, such as thread pool, cache, registry, log object, etc. at this time, it is the best choice to design it as singleton mode.
1. Six implementation methods of singleton mode
1) Lazy mode (thread unsafe)
public class Singleton01 { private static Singleton01 instance; /** * Private construction method */ private Singleton01(){} public static Singleton01 getInstance() { if(instance == null) { instance = new Singleton01(); } return instance; } }
This writing method realizes delayed loading, but the thread is not safe. No use!
2) Lazy mode (thread safe)
public class Singleton02 { private static Singleton02 instance; /** * Private construction method */ private Singleton02(){} public static synchronized Singleton02 getInstance() { if(instance == null) { instance = new Singleton02(); } return instance; } }
This writing method realizes delayed loading and adds synchronized to ensure thread safety, but the efficiency is too low. Not recommended
3) Lazy mode (double check lock)
public class Singleton03 { private volatile static Singleton03 instance; /** * Private construction method */ private Singleton03(){} public static Singleton03 getInstance() { if(instance == null) { synchronized (Singleton03.class) { if (instance == null) { instance = new Singleton03(); } } } return instance; } }
volatile mechanism is used. This is an upgraded version of the second method, commonly known as double check locking. It ensures both efficiency and safety.
4) Hungry man model
public class Singleton03 { private static Singleton03 instance = new Singleton03(); /** * Private construction method */ private Singleton03(){} public static synchronized Singleton03 getInstance() { return instance; } }
This class based loading mechanism avoids the synchronization problem of multithreading and is loaded at the time of initialization. But it doesn't have the effect of lazy loading.
This is also the simplest implementation.
5) Static inner class
public class Singleton04 { // Static inner class private static class SingletonHolder { private static final Singleton04 INSTANCE = new Singleton04(); } /** * Private construction method */ private Singleton04(){} public static Singleton04 getInstance() { return SingletonHolder.INSTANCE; } }
In this way, when the Singleton04 class is loaded, its internal class will not be loaded, so the singleton class INSTANCE will not be initialized.
SingletonHolder is loaded only when getInstance method is explicitly called, thus instantiating INSTANCE.
Since the instance is created when the class is loaded, it is inherently thread safe. Therefore, it has the characteristics of lazy loading and thread safety.
6) Enumeration (claimed best)
public enum EnumSingleton01 { INSTANCE; public void doSomething() { System.out.println("doSomething"); } }
Simulation database link:
public enum EnumSingleton02 { INSTANCE; private DBConnection dbConnection = null; private EnumSingleton02() { dbConnection = new DBConnection(); } public DBConnection getConnection() { return dbConnection; } }
This method is advocated by Josh Bloch, author of Effective Java. It can not only avoid the problem of multi-threaded synchronization,
It also prevents deserialization and re creation of new objects.
2. Why is enumeration the best method?
The first five methods have the following three characteristics:
- Construction method privatization
- Instantiated variable reference privatization
- Methods for obtaining instances are
First, privatizing the constructor is not safe. Because it cannot resist reflection attacks, the second is serialization and re creation of new objects. Let's verify.
1) Reflection verification
@Test public void reflectTest() throws Exception { Singleton03 s = Singleton03.getInstance(); // Get all constructors, including non-public ones Constructor<Singleton03> constructor = Singleton03.class.getDeclaredConstructor(); constructor.setAccessible(true); // Construction example Singleton03 reflection = constructor.newInstance(); System.out.println(s); System.out.println(reflection); System.out.println(s == reflection); }
Output results:
org.yd.singleton.Singleton03@61e4705b org.yd.singleton.Singleton03@50134894 false
Let's look at the test of enumeration classes
@Test public void reflectEnumTest() throws Exception { EnumSingleton01 s = EnumSingleton01.INSTANCE; // Get all constructors, including non-public ones Constructor<EnumSingleton01> constructor = EnumSingleton01.class.getDeclaredConstructor(); constructor.setAccessible(true); // Construction example EnumSingleton01 reflection = constructor.newInstance(); System.out.println(s); System.out.println(reflection); System.out.println(s == reflection); }
Output results:
java.lang.NoSuchMethodException: org.yd.singleton.EnumSingleton01.<init>() at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getDeclaredConstructor(Class.java:2178) at org.yd.singleton.SingletonTest.reflectEnumTest(SingletonTest.java:61) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Conclusion: through reflection, the private construction method of singleton pattern can also construct new objects. unsafe. The enumeration class throws exceptions directly, which shows that the enumeration class is safe for reflection.
2) Serialization validation
@Test public void serializeTest(){ Singleton03 s = Singleton03.getInstance(); String serialize = JSON.toJSONString(s); Singleton03 deserialize =JSON.parseObject(serialize,Singleton03.class); System.out.println(s); System.out.println(deserialize); System.out.println(s == deserialize); }
Output results:
org.yd.singleton.Singleton03@387c703b org.yd.singleton.Singleton03@75412c2f false
Conclusion: the two objects before and after serialization are not equal. So serialization is also unsafe.
Also look at the test of enumeration classes
@Test public void serializeEnumTest(){ EnumSingleton01 s = EnumSingleton01.INSTANCE; String serialize = JSON.toJSONString(s); EnumSingleton01 deserialize =JSON.parseObject(serialize,EnumSingleton01.class); System.out.println(s); System.out.println(deserialize); System.out.println(s == deserialize); }
Output results:
INSTANCE INSTANCE true
Conclusion: the enumeration class serialization is safe.
To sum up, it can be concluded that enumeration is the best practice to implement singleton mode.
- Reflection safety
- Serialization / deserialization security
- Simple writing