define
The singleton is a “simple” pattern, defined as follows:
Ensure that a class has only one instance and provide a global access point to access it.
or
Ensure a class has only one instance, and provide a global point of access to it.
Ensure that there is only one instance of a class, and that you instantiate it yourself and make it available to the entire system.
Notice the double quotation marks around the word “simple”. It is easy to call it simple, but it is not that simple to use well and correctly. Why do you say that?
- Firstly, the definition of singleton pattern is easy to understand, the application scenario is clear, and the implementation idea is relatively simple.
- Second, there are many factors to consider in the singleton pattern, such as lazy loading, thread safety, and broken singleton situations. Just because of these factors, the singleton pattern has its advantages and disadvantages
The characteristics of
- A singleton class can have only one instance;
- A singleton class must create its own unique instance;
- The singleton class must provide this instance to all other objects.
Basic steps
- Private static member variables: create a unique instance of the class and store it using static member variables; For security, privatize the member variable
- Private constructor: Avoid objects that other classes can create directly from a singleton class
- Public static methods: used by other classes to obtain unique instances of this class
Factors to consider
-
Lazy loading
-
Thread safety
-
Case of breaking singletons
-
serialization
If the Singleton class is Serializable, it is not enough to simply add Implements Serializable to the generation declaration. To maintain and ensure Singleton, you must declare all instance domains transient and provide a readResolve method. Otherwise, a new object is created each time a serialized instance is deserialized.
-
reflection
Authorized by the client can through reflection to invoke the private constructor, with the aid of AccessibleObject. SetAccessible method can do it. If you need to protect against this attack, modify the constructor to throw an exception when asked to create a second instance.
private Singleton(a) { System.err.println("Singleton Constructor is invoked!"); if(singleton ! =null) { System.err.println("Instance already exists, cannot initialize!"); throw new UnsupportedOperationException("Instance already exists, cannot initialize!"); }}}Copy the code
-
Object replication
In Java, objects cannot be copied by default. If the Cloneable interface is implemented and the Clone method is implemented, a new object can be created by object copy directly. Object copy does not call the constructor of the class, so the object can be copied even if the constructor is private. In general, class replication is not a concern. It is rare for a singleton class to ask to be copied. The best way to solve this problem is for the singleton class not to implement the Cloneable interface.
-
Class loader
If a singleton is loaded by different class loaders, it is possible to have multiple instances of a singleton class.
-
implementation
1. Slacker style
Thread unsafe (single thread)
public class Singleton {
private static Singleton singleton;
private Singleton(a) {}public static Singleton getInstance(a) {
if (singleton == null) {
singleton = new Singleton();
}
returnsingleton; }}Copy the code
- Advantages: Lazy loading
- Disadvantages: Thread insecurity, multi-threaded environment may produce multiple instances
To solve the lazy thread-safety problem, we can set getInstance() to a synchronous method, so there is a second implementation:
Thread safety
public class Singleton {
private static Singleton singleton;
private Singleton(a) {}public static synchronized Singleton getInstance(a) {
if (singleton == null) {
singleton = new Singleton();
}
returnsingleton; }}Copy the code
- Advantages: Lazy loading, and thread safety
- Cons: Very inefficient, 99% of the time there is no synchronization required
hungry
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(a) {}public static Singleton getInstance(a) {
returnsingleton; }}Copy the code
- Advantages: Thread safety, simple implementation
- Disadvantages: No lazy loading, class initialization is completed at load time, may cause a certain amount of memory space waste
If lazy-loading scenarios are not particularly required, hangry is preferred
3. Double check the lock
public class Singleton {
private static volatile Singleton singleton;
private Singleton(a) {}public static Singleton getInstance(a) {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = newSingleton(); }}}returnsingleton; }}Copy the code
-
Pros: Lazy loading, thread-safe, and very efficient
-
Cons: implementation is a bit more complex, volatile is not supported until JDK1.5
-
instructions
- Change the synchronization method to synchronize code blocks
- The first null-out is an efficiency issue that doesn’t require entering a synchronized code block every time
- Synchronized (Singleton.class) is designed to address thread safety issues
- The second null is to avoid creating multiple instances
- The volatile modifier disallows instruction reordering
A few more words about volatile. Many examples of double-checked locking in books and on the Web do not use volatile, which is actually not true
First, volatile has two meanings:
- Memory visibility
- Disallow command reordering
It is mainly the second semantics that we use here. Instruction reordering is a process by which compilers and processors sort sequences of instructions to optimize program performance. Simple understanding, is that the compiler to optimize our code, the actual execution of instructions may be different from the order we wrote, only to ensure that the program execution result is the same as the source code, but does not guarantee that the order of the actual instructions is the same as the source code.
singleton = new Singleton();
This code actually takes three steps when executed by the JVM:
- Create a space in heap memory.
- Instantiate the Singleton in heap memory
- Point the object (singleton) to the heap memory space
Due to the “instruction rearrangement” optimization, it is likely that the step is 1-3-2, i.e., the object has not been instantiated but the reference is non-empty, i.e., false in the second nullation, and returns singleton — an object reference that has not been instantiated.
Here involves the Java memory model, memory barrier and other knowledge points, this article mainly introduces the singleton pattern, so it will not be described, interested students can search for their own
4. Static inner classes
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(a) {}public static Singleton getInstance(a) {
returnSingletonHolder.INSTANCE; }}Copy the code
The difference is that the static inner class SingletonHolder is loaded only when the getInstance() method is called for the first time (implementing the lazy loading effect).
Therefore, static inner class implementation can not only ensure thread-safety, but also ensure the uniqueness of singletons, and has the characteristics of lazy loading
5, the enumeration
public enum Singleton {
INSTANCE;
public void doSomething(a) {
System.out.println("doSomething"); }}Copy the code
Advantages: The enumeration approach has the advantages of all of the above implementations, but also provides a serialization mechanism for free, preventing multiple instantiations
Disadvantages: support enum only after JDK1.5; The popularity is not high compared with the previous ways
advantages
- Because the singleton pattern has only one instance in memory, it reduces the memory overhead, especially when an object needs to be created and destroyed frequently, and performance cannot be optimized during creation or destruction.
- Because the singleton mode generates only one instance, it reduces the system performance overhead. When more resources are needed to generate an object, such as reading configuration or generating other dependent objects, a singleton can be generated directly when the application is started. Then fix it by permanently resident memory (note the JVM garbage collection mechanism when using singleton in Java EE).
- The singleton mode can avoid multiple resource usage, such as a write file operation. Because only one instance exists in memory, simultaneous write operations on the same resource file are avoided.
- The singleton pattern can set up global access points in the system, optimize and share resource access, for example, you can design a singleton class, responsible for all data table mapping processing.
disadvantages
- The singleton pattern generally has no interface and is difficult to extend, and there is basically no other way to extend it than by modifying the code. Why can’t singletons add interfaces? Because the interface makes no sense to the singleton pattern, it requires “self-instantiation” and provides a single instance, interface, or abstract class that cannot be instantiated. Of course, in special cases, singletons can implement interfaces, be inherited, etc., which need to be judged according to the environment in system development.
- The singleton pattern is bad for testing. In a parallel development environment, you can’t test until the singleton pattern is complete, and you can’t mock an object without an interface.
- The singleton pattern conflicts with the single responsibility principle. A class should implement only one logic, regardless of whether it is a singleton or not, depending on the context. The singleton pattern combines “singleton” and business logic in a single class.
Usage scenarios
In a system, a class is required to have only one object. If there are multiple objects, there will be “adverse reactions”. Singleton mode can be adopted, and the specific scenarios are as follows:
- An environment that requires a unique sequence number to be generated;
- A shared access point or shared data is needed throughout the project, such as a counter on a Web page, which can be saved from logging every refresh to the database, using a singleton pattern to keep the value of the counter and ensure that it is thread-safe;
- Creating an object consumes too many resources, such as accessing IO and database resources.
- Environments where you need to define a large number of static constants and static methods (such as utility classes) can use the singleton pattern (or, of course, declare it static).
Source code address: gitee.com/tianranll/j…
References: Zen of Design Patterns, Effective Java