Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
The singleton design pattern means that only one instance can exist in the entire system, such as a log object. There are two ways to create singletons: hungry and lazy. Today we’ll expand our thinking and look at a few more.
First of all, if we want a class with only one object, we must first privatize the constructor and stop using it to create instances in other classes. We can’t create an instance in any other class, so we have to create a private instance in the class and provide a common method for other objects to get the instance. So, here comes the first edition.
1. 【 hungry 】
The instance is created when the class is loaded
@ThreadSafe
public class SingletonExample2 {
// Privatize constructor
private SingletonExample2(a){}
// Provide an example
private static SingletonExample2 instance = new SingletonExample2();
// Provide a common method return instance
public static SingletonExample2 getInstance(a){
returninstance; }}Copy the code
Don’t forget that in a multi-threaded environment there’s also a concern about thread safety, and I’ll annotate it with @ThreadSafe for thread safety and @NotThreadSafe for thread safety.
The above method is relatively simple and the easiest to think of, but it has the disadvantage of wasting resources if we do not use this object, which may not be used, but we have already created it.
2 【 hungry 】
This approach is to create a singleton with the help of “static code blocks are only loaded once”, which is very simple and understandable, but the problem is just like hungry, you don’t necessarily use this object, so it can be a waste of resources.
@ThreadSafe
public class SingletonExample6 {
// Privatize constructor
private SingletonExample6(a){}
private static SingletonExample6 instance = null;
static {
instance = new SingletonExample6();
}
// Provide a common method return instance
public static SingletonExample6 getInstance(a){
returninstance; }}Copy the code
3. 【 lazy 】
Instances are created only when objects are in use
**@NotThreadSafe
public class SingletonExample1 {
// Privatize constructor
private SingletonExample1(a){}
// Provide an example
private static SingletonExample1 instance = null;
// Provide a common method return instance
public static SingletonExample1 getInstance(a){
if(instance == null) {return new SingletonExample1();
}
returninstance; }} * *Copy the code
If thread A enters if and then suspends execution, then another thread B can enter if and return an instance. When thread A gets executed again, it will return another instance.
4 【 lazy V2】
Add the synchronized keyword to the common method to synchronize the method. Feasible, but not recommended, because after the synchronized modification method, only one thread can execute the method at the same time. Once a thread obtains the method, other threads need to wait, which will waste a lot of time and reduce the system operation efficiency.
@ThreadSafe
@NotRecommend
public class SingletonExample3 {
// Privatize constructor
private SingletonExample3(a){}
// Provide an example
private static SingletonExample3 instance = null;
// Provide a common method return instance
public static synchronized SingletonExample3 getInstance(a){
if(instance == null) {return new SingletonExample3();
}
returninstance; }}Copy the code
5. [slacker V3]
This approach is thread-safe by double-checking and preventing instruction reordering. The first thing to note is that in getInstance, we need double-checking and synchronizing the object creation process with synchronized code blocks.
@NotThreadSafe
public class SingletonExample4 {
// Privatize constructor
private SingletonExample4(a){}
// Provide an example
private static SingletonExample4 instance = null;
// Provide a common method return instance
public static SingletonExample4 getInstance(a){
// Thread B finds that instance is not empty and returns instance that has not been initialized.
if(instance == null) {// Dual detection mechanism
synchronized (SingletonExample4.class) { / / synchronization locks
if(instance == null) {// Thread A executes to rearranged instruction 3, at which point instance already has the address value. But there is no initialization
return new SingletonExample4(); // This is important!!}}}returninstance; }}Copy the code
Because in the course of new SingletonExample4(), it is not an atomic operation, it can be further broken down into:
1. Allocate object memory space
memory = allocate()
2. Initialize the object
initInstance()
Set instance to the newly allocated memory
instance = memory
In the case of multi-threading, there will be instruction reordering in the above three instructions. [JVM and CPU instructions optimization] The result of the rearrangement may be:
memory = allocate()
instance = memory
initInstance()
At this time, thread A may execute the third step after the instruction rearrangement in the inner layer if, but it does not initialize, only the address value exists. Thread B will directly return the instance when determining the outer layer IF, and this instance is an instance that only has the address value but has not been initialized.
To prevent instruction reordering problems, use the volatile keyword. This is thread-safe. Just use volatile to modify instance instances from the previous version.
The semantics of volatile are to add memory barriers and prevent instruction reordering, as analyzed previously.
private static volatile SingletonExample4 instance = null;
Copy the code
6 using enumeration classes to implement singleton pattern
This is recommended because it is easier to guarantee than slacker thread-safety, and it is higher performance than hungry, which only instantiates objects when called.
@ThreadSafe
@Recommend
public class SingletonSpecial {
private SingletonSpecial(a){}
public static SingletonSpecial getInstance(a){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
// public static final Singleton INSTANCE;
private SingletonSpecial singleton;
// The JVM ensures that the constructor is called only once
Singleton(){
singleton = new SingletonSpecial();
}
public SingletonSpecial getInstance(a){
returnsingleton; }}}Copy the code
7 [Using static inner class]
This method does not instantiate the Singleton class immediately when it is loaded. Instead, the SingletonInstance class is loaded when it needs to be instantiated by calling the getInstance method.
With the static final modifier, the JVM guarantees that instance will only be initialized once and will not change.
@ThreadSafe
@Recommend
public class SingletonExample7 {
private SingletonExample7(a){}
private static class SingletonInstance{
private static final SingletonExample7 instance = new SingletonExample7();
}
public static SingletonExample7 getInstance(a){
returnSingletonInstance.instance; }}Copy the code
To sum up, today’s focus is on the implementation of the singleton pattern, and in the middle, a review of the previous thread safety application.