Singleton mode
The common writing methods of single case mode include lazy mode, hungry mode, double check mode, etc.
- Lazy mode is to create objects when you use them.
- Hungry man mode is a static object that has been loaded in advance.
- Double check mode is to check twice before and after locking to prevent multiple threads from creating multiple objects.
The singleton mode has the following characteristics:
- A singleton class can only have one instance.
- A singleton class must create its own unique instance.
- A singleton class must provide this instance to all other objects.
Advantages: objects will not be created and destroyed frequently, wasting system resources
Singleton mode in the case of single thread
class Singleton { private static Singleton instance = null; private Singleton() { System.out.println("I am the construction method" + Thread.currentThread().getName()); } static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); Singleton instance3 = Singleton.getInstance(); System.out.println(instance1 == instance2); System.out.println(instance2 == instance3);
Operation results:
I am the construction method main true true
Singleton mode in multithreading
It can be seen that in the case of single thread, this writing method meets the requirements of singleton mode, and the construction method is called only once.
So let's try again in a multithreaded environment
class Singleton { private static Singleton instance = null; private Singleton() { System.out.println("I am the construction method" + Thread.currentThread().getName()); } static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { Singleton.getInstance(); }, String.valueOf(i)).start(); } } }
Operation results:
I am constructor 0 I am construction method 4 I am construction method 3 I am construction method 2 I am construction method 1
This is because in the case of multithreading, when the instance object has not been created, multiple threads enter the code for creating the object (because they judge that instance == null at this time), so multiple instances are created from new
Of course, this obviously does not meet the requirements of the singleton mode, so we introduce the singleton mode in the double ended check lock mode
Singleton mode in DDL mode
- D:Double
- C:Check
- L:Lock
Looking at the following code, we can see that instance ==null is judged before and after locking, so it is called double end locking.
class SingletonDCL { private static SingletonDCL instance = null; private SingletonDCL() { System.out.println("I am the construction method" + Thread.currentThread().getName()); } static SingletonDCL getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new SingletonDCL(); } } } return instance; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { SingletonDCL.getInstance(); }, String.valueOf(i)).start(); } } }
Operation results:
I am constructor 0
We can see that the running result is what we want. Only one instance is created in a multithreaded environment. Although the double ended check lock greatly reduces the probability of creating multiple instances, there are still some problems.
Existing shortcomings
When a thread reads that instance is not empty, the reference object of instance may not have completed initialization, which is due to instruction rearrangement.
instance = new SingletonDCL(); This process can be divided into the following three steps:
- memory = allocate(); //1. Allocate object memory space
- instance(memory);//2. Initialization object
- instance = memory;//3. Set instance to point to the memory address of the object just allocated. At this time, instance= null;
However, in a single thread environment, there is no data dependency between 2 and 3, so instruction rearrangement may occur,
- memory = allocate(); //1. Allocate object memory space
- instance = memory;//3. Set instance to point to the memory address of the object just allocated. At this time, instance=
null, but the object has not been initialized yet. - instance(memory);//2. Initialization object
So if a thread accesses instance at this time= Null, because the object has not been initialized, it will return a null value, which causes thread safety problems. Therefore, we need to add volatile to prevent instruction rearrangement
private volatile static SingletonDCL instance = null;
summary
Safe singleton mode in multithreaded environment = double ended check lock + volatile
Of course, synchronized can also be added to the method, but this locks the whole method and reduces the performance. It is not recommended.