1, Brief overview
- Singleton mode is to take certain methods to ensure that there can only be one object instance for a class in the whole software system, and the class only provides a static method to obtain the instance.
- The singleton pattern ensures that a class has only one instance, and will instantiate itself and provide this instance to the whole system to ensure its uniqueness.
Two. Pattern structure
It is usually implemented using a private constructor, a private static variable, and a public static function
3, Implementation mode
1. Hungry Chinese implementation
Thread safety, high calling efficiency, can not delay loading, and may cause a waste of resources
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } // Realize one class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return this.instance; } } // Implementation II class Singleton{ private final static Singleton instance; static{ instance = new Singleton(); } private Singleton(){ } public static Singleton getInstance(){ return this.instance; } }
2. Lazy implementation
Single thread is safe, but in the case of multithreading, it is not safe, the calling efficiency is not high, the load can be delayed, and the resources can be saved
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } // Thread unsafe public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } // Thread safety public class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
3. Realization of double detection lock
Thread safety, high calling efficiency, delay loading and resource saving
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { // First judge whether instance has been instantiated. If not, lock the instantiation statement if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
[note]: instruction rearrangement may occur in the JVM, that is, in a multithreaded environment, an object may be allocated an address space but not initialized, so it is possible to create multiple objects. volatile keyword can solve the problem of instruction rearrangement and ensure that the variable value will be synchronized to main memory as soon as it changes.
4. Static internal class implementation
Thread safety, high calling efficiency, delay loading and resource saving
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } public class Singleton { private Singleton() { } private static class InnerSingleton { private final static Singleton instance = new Singleton(); } public static Singleton getInstance() { return InnerSingleton.instance; } }
5. Enumeration class implementation
Thread safety, high calling efficiency, can not delay loading, and may cause a waste of resources
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.INSTANCE(); Singleton s2 = Singleton.INSTANCE(); System.out.println(s1 == s2); } } public enum Singleton { INSTANCE; private String name; public String getName { return this.name; } public void setName(String name) { this.name = name; } }
[note]: this method is advocated by Effective Java author Josh Bloch. It can not only avoid the problem of multi-threaded synchronization, but also automatically support the serialization mechanism to prevent reflection or deserialization from damaging singleton.
4, Precautions
-
For the first four implementations mentioned above, we can actually destroy this singleton by means of reflection or deserialization.
Destruction by reflection
public class TestSingleton{ public static void main(String[] args){ Constructor<?> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton s1 = constructor.newInstance(); Singleton s2 = constructor.newInstance(); System.out.println(s1 == s2); } } class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return this.instance; } }
Destruction by deserialization
public class TestSingleton{ public static void main(String[] args){ Singleton s1 = Singleton.getInstance(); FileOutputStream fOutputStream = new FileOutputStream("Serialized file location"); ObjectOutputStream outputStream = new ObjectOutputStream(fOutputStream); outputStream.writeObject(s1); FileInputStream fInputStream = new FileInputStream("Serialized file location"); ObjectInputStream inputStream = new ObjectInputStream(fInputStream); Singleton s2= (Singleton)inputStream.readObject(); System.out.println(s1 == s2); } } class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return this.instance; } }
-
For the enumeration implementation, it is a singleton mode, and it will not be affected by reflection or deserialization due to the security guarantee provided by the JVM.
5, Advantages and benefits
- Since the singleton pattern only generates one instance, the system performance overhead is reduced, especially when generating large objects
- Since the singleton mode sets the global access point in the system, the shared resource access is optimized
6, How to choose
- When the created object occupies less resources and does not pay attention to latency, hungry Han style or enumeration class is selected
- When the created objects occupy more resources and pay attention to latency, lazy or static internal classes or double detection locks are selected
7, Application scenario
- The application only needs one instance object of this class
- Only one access point is allowed when a client calls an instance
For example, the Runtime class in the JDK source code