define
Ensure that a class has only one instance and provide a global access point to access it.
Hangry singleton
Classes are instantiated when they are initialized
public class Singleton {
// Private static member variables
private static Singleton instance = new Singleton();
// Private constructor
private Singleton(a){}
Public static access methods
public static Singleton getInstance(a)
{
returninstance; }}Copy the code
This way the initialization is done at class load time, so class load is slower, but object retrieval is faster.
Lazy singleton
It is instantiated the first time the instance is called
public class Singleton {
// Private static member variables
private static Singleton instance = null;
// Private constructor
private Singleton(a){}
// Public static access methods, note that the synchronized keyword to ensure thread safety
public static synchronized Singleton getInstance(a)
{
if(instance == null)
instance = new Singleton();
returninstance; }}Copy the code
This works well in multiple threads, but synchronization is required every time the getInstance method is called, incurring unnecessary synchronization overhead, and most of the time we don’t need synchronization, so it’s not recommended.
Double check lock
public class Singleton {
private static Singleton sInstance;
public static Singleton getInstance(a) {
if (sInstance == null) {/ / position 1
synchronized (Singleton.class) {
if (sInstance == null) {
sInstance = newSingleton();/ / position 2}}}return sInstance;
}
private Singleton(a) {}}Copy the code
In multithreaded environments, getInstance() checks whether the singleton has been initialized. If it has been initialized, it returns the singleton directly. If it has not been initialized, it initializes the singleton in the synchronized code block and then returns it.
But this is a bad optimization, and the root of the problem is position 2
sInstance =new Singleton(); This statement creates an object that can be decomposed into three lines of code:
memory = allocate();// 1. Allocate the memory space of the objectctorInstance(memory);// 2. Initialize the objectsInstance = memory;// 3. Set sInstance to the newly allocated memory address
Copy the code
Reordering may occur between 2 and 3 in the pseudocode above, and the order of execution after reordering is as follows:
memory = allocate();// 1. Allocate the memory space of the objectsInstance = memory;// 2. Set sInstance to the newly allocated memory address before the object is initializedctorInstance(memory);// 3. Initialize the object
Copy the code
Because this reordering does not affect the specification in the Java specification: Intra-Thread Sematics allows reordering that does not change the results of a single-threaded program execution within a single thread.
But the following can happen when multiple threads are running concurrently:Thread B accesses an uninitialized object.
Solution 1:
public class Singleton {
private static volatile Singleton sInstance;
public static Singleton getInstance(a) {
if (sInstance == null) {
synchronized (Singleton.class) {
if (sInstance == null) {
sInstance = newSingleton(); }}}return sInstance;
}
private Singleton(a) {}}Copy the code
When the object is specified as volatitle, reordering is disabled in multi-threaded environments
Solution 2:
public class Singleton {
private Singleton(a){};
private static class Inner{
private static Singleton SINGLETION=new Singleton();
}
public static Singleton getInstance(a){
returnInner.SINGLETION; }}Copy the code
A static Inner class is not initialized when the outer class is initialized. It is loaded and initialized separately. The Inner class is initialized when the getInstance method is first executed.
The static object SINGLETION is initialized during the Inner class initialization phase, which is when the virtual machine executes the class constructor < Clinit >() method.
The virtual machine ensures that a class’s < Clinit >() methods are locked and synchronized correctly in a multithreaded environment. If multiple threads initialize a class at the same time, only one thread will execute the class’s < Clinit >() methods, and all other threads will block and wait.
The enumeration
public enum Singleton {
INSTANCE;
public void doSomeThing(a) {}}Copy the code
The default creation of enumerated instances is thread-safe and singleton in any case. One of the implementations of the singleton pattern described above where they recreate the object is deserialization, where a singleton object is written to disk and then read back, thus obtaining an instance. The deserialization operation provides the readResolve method, which gives developers control over deserialization of objects. In the examples above, to prevent the deserialization of the singleton or to regenerate the object, the following methods must be added:
private Object readResolve(a) throws ObjectStreamException {
return singleton;
}
Copy the code
Refer to the article
- Seven ways to write a singleton pattern
- Problems with traditional singleton double-checked locking