Ensure that there is only one instance of a certain class in the program instead of creating multiple instances, which will improve efficiency.
In the single-interest mode, only one getInstance() method is generally provided to obtain the instance object, and the setInstance() method is not provided. The purpose is to avoid instantiating other instance objects.
There are two modes in the singleton mode, one is the hungry man mode and the other is the lazy man mode.
The Hungry Han Mode means that the class will be instantiated immediately when it is loaded, and will only appear in subsequent uses. An example.
package thread.example; //饿汉模式 public class HungrySingle { //在类加载的时候就实例化了,类加载只有一次,所以值实例化出了一份该实例对象 private static HungrySingle instance = new HungrySingle(); public static HungrySingle getInstance() { return instance; } }
It has already been instantiated when the class is loaded, so the instantiation is not involved. The instantiated modification operation is only a read operation. It is thread-safe in multi-threaded situations.
is not instantiated directly when the class is loaded, but is instantiated when the specified instance method is called. , this will ensure that it will not be instantiated when you don’t want to use it. Generally speaking, it is more efficient than the Hungry Man mode.
package thread.example; //单线程的懒汉模式 public class LazySingle { private static LazySingle instance = null; //只有在调用该方法的时候才实例化 public static LazySingle getInstance() { if(instance == null) { instance = new LazySingle(); } return instance; } }
(1) Causes the inconsistency of lazy mode in multi-threaded situations Security reasons
In the case of multi-threading, both threads may get a copy of instance=null. This is because if thread 1 modifies the instance in its own county, it has not had time to modify the instance in the main memory. instance, causing thread 2 to also instantiate an instance object, and it is no longer a singleton mode at this time. The main cause of this problem is that it involves modifying the instance and losing atomicity. In order to ensure atomicity, we thought of locking to achieve thread safety.
(2) Solution code example
Version 1
package thread.example; //多线程安全下的懒汉模式 public class LazySingle { private LazySingle() { } private static LazySingle instance = null; //只有在调用该方法的时候才实例化 public static synchronized LazySingle getInstance() { if (instance == null) { instance = new LazySingle(); } return instance; } }
Although the code in version 1 ensures thread safety, locking will still occur every time the method is called. Regarding the unlocking problem, in order to further optimize it, we can reduce the granularity of the lock to improve efficiency, because after adding a lock, we will no longer be able to achieve high concurrency, but we still want to improve efficiency, so we will optimize it.
Version 2
Double if judgment locking improves efficiency
package thread.example; public class SecurityLazyModle { private LazySingle() { } private static volatile SecurityLazyModle instance = null;//保证内存可见性,防止编译器过度优化(指令重排序) public static SecurityLazyModle getInstance() { if(instance == null) { synchronized (SecurityLazyModle.class) { if(instance == null) { instance = new SecurityLazyModle(); } } } return instance; } }
Explanation of version 2
The first layer of if is to determine whether the current When the instance is created, the second level of synchronized is to enable the threads that enter the current if to compete for the lock. When the thread that gets the lock enters the third level of if, it determines whether it is empty. If it is not empty, it instantiates the object and then releases it. Lock, after the lock is released, the instance is no longer empty, and subsequent threads are blocked at the third layer if. Later, when accessing the getInstance() method, it is found that the instance is no longer empty, so there is no need to preempt it. Lock resources, because competing locks also consume a lot of time. By processing in this way, thread safety is ensured and efficiency is improved.
The purpose of using volatile here is to prevent instruction reordering caused by compiler optimization. Performing a new object is not an atomic operation and can be divided into three steps:
1. Allocate memory space
2. Instantiate objects
3.Assign values to variables
For the above execution, if 1 and 3 are executed first (assuming 2 has not been completed yet), the thread outside the first layer of if will judge that it is not null at this time, and the object will be returned directly at this time. But this object is only half executed, and subsequent use will cause thread safety issues.
Volatile can ensure that these three steps must be executed (regardless of the order, they will eventually be executed) before external threads can execute. At this time, the integrity of the object is guaranteed.
The above is the detailed content of Detailed explanation of how to implement the Hungry Man and Lazy Man patterns in Java singleton mode. For more information, please follow other related articles on the PHP Chinese website!