Singleton pattern: seven implementations of singleton

What is the singleton pattern?

Singleton pattern: A class can only have one instance, providing a global access point.
Whenever possible, make sure that a class has only one instance, and that it is self-instantiated and provides this instance to the entire system. Such a class is called a singleton class, which provides global access methods.
Singleton pattern is an object creation pattern.

Why does the singleton pattern exist?

What is the significance of the existence of the singleton model? Simply put, what are the requirements scenarios for the singleton pattern?
In fact, the single instance mode is very common in our common use, such as we often call a connection pool of some data.
That is the singleton mode. In order to ensure that there is only one connection pool in the whole system, all people access the same connection pool.
Why do we need to ensure that there is only one such object in the system?
In order to save resources, take our database connection pool as an example. You manage 50 links in one connection pool and 25 links in two connection pools. In fact, the complexity of using is different. If two are filled, if one is filled, you choose the one that is not full; at the same time, you don't know when it will be. This leads you to judge every time you use the connection pool, which undoubtedly increases the complexity of the operation and is unnecessary. Moreover, the system generates two objects which cause unnecessary waste of resources, and the stack needs to open up space. These are unnecessary waste of resources.
And log, which is a typical singleton. In every java class, just make sure that only the corresponding copy is needed.
Significance: Reduce memory overhead and save system resources.

How can the singleton pattern be implemented?

(1) Hungry Han Thought

This ensures that there is only one instance, because the instance is created when the class is initialized.
Disadvantage: heap memory resides for a long time, but is not necessarily used.

/**
 * hungry mode 
 * @author wanghanwei.wb
 */
public final class SingletonHungry {

	private byte[] date = new byte[1024];
	
	private static SingletonHungry instance = new SingletonHungry();
	
	private SingletonHungry() {
		
	}
	
	public static SingletonHungry getInstance() {
		return instance;
	}
}

(2) Lazy Mentality

Advantages: Be instantiated when needed
Disadvantage: Thread security issues exist.

public final class SingletonLazy {
	
	private byte[] date = new byte[1024];
	
	private static SingletonLazy instance = null;
	
	private SingletonLazy() {
		
	}
	public SingletonLazy getInsatnce() {
		if(instance == null) {
			instance = new SingletonLazy();
		}
		return instance;
	}
}
(3) Lazy Mentality + Synchronization Method

Advantages: No concurrency issues
Disadvantage: Only one thread can get access to getInstance() method at a time, while other threads can only wait while accessing at the same time, resulting in poor performance.

public final class SingletonSync {
	private byte[] data = new byte[1024];
	private static SingletonSync instance = null;
	private SingletonSync() {
		
	}

	public static synchronized SingletonSync getInstance() {
		if (instance == null) {
			instance = new SingletonSync();
		}
		return instance;
	}
}

(4) Implementation of double check

Advantages: While solving the problem of high concurrency, it also solves the problem of having only one thread access at a time.
Disadvantage: Because the CPU's instructions are rearranged, there are still risks.

/**
 * double check 
 * @author wanghanwei.wb
 *
 */
public class SingletonDoubleCheck {
	private byte[] data = new byte[1024];
	private static SingletonDoubleCheck instance = null;
		private SingletonDoubleCheck() {
	}

	public static SingletonDoubleCheck getInstance() {
//		first check
		if (instance == null) {
		// To ensure that all threads use the same single instance object, you need to lock the class object.
			synchronized (SingletonDoubleCheck.class) {
//				second check
				if (instance == null) {
					instance = new SingletonDoubleCheck();
				}
			}
		}
		return instance;
	}
}
(5) Implementation of double check security

Potential danger of double check: Problems caused by the rearrangement of cpu instructions: for example instance is no longer null, but there is still no code to execute the instantiation, so the acquisition of conn and socket is still null, and the user will have null value exceptions.
So you may be confused, but the important thing is the new process:
Here we quote "Geek Time - Java Concurrent Programming Practice - 01 | Visibility, Atomicity and Orderliness: The Source of Concurrent Programming Bug"

We think the new operation should be:
Allocate a memory M;
Initialize the Singleton object on memory M;
Then the address of M is assigned to the instance variable.

But in fact, the optimized execution path is as follows:
Allocate a memory M;
The address of M is assigned to instance variable.
Finally, the Singleton object is initialized on memory M.

So when the reference instance is not empty, there is still no null in the heap memory, but when other threads judge instance = null, they will not judge null, and they will let other threads execute other logic.
error code

import java.net.Socket;
import java.sql.Connection;
/**
 * 		There is an empty pointer when other objects are used because of instruction rearrangement and other objects have not been initialized.
 * @author wanghanwei.wb
 *
 */
public final class SingletonDoubleCheckDanger {
	
	private byte[] data = new byte[1024];
	
	private static SingletonDoubleCheckDanger instance  = null;
	
	Connection conn;
	
	Socket socket;
	
	private SingletonDoubleCheckDanger() {
		this.conn = null;// Initialize conn, where null is used instead of step, as you will see.
		this.socket  = null;// Initialize socket, null instead of step, you will understand.
	}
	
	public static SingletonDoubleCheckDanger getInstance() {
		if (instance == null) {
//			To ensure that all threads use the same single instance object, you need to lock the class object.
			synchronized (SingletonDoubleCheckDanger.class) {
				if (instance == null) {
					instance = new SingletonDoubleCheckDanger();
				}
			}
		}
		return instance;
	}
}

Keyword volatile is used to prevent instruction rearrangement.
Correct code

import java.net.Socket;
import java.sql.Connection;

public class SingletonDoubleCheckSafe {
	
	private byte[] data = new byte[1024];
	//Keyword to prevent instruction rearrangement
	private volatile static SingletonDoubleCheckSafe instance  = null;
	
	Connection conn;
	
	Socket socket;
	
	private SingletonDoubleCheckSafe() {
		this.conn = null;// Initialize conn
		this.socket  = null;// Initialize socket
	}
	
	public static SingletonDoubleCheckSafe getInstance() {
		if (instance == null) {
//			To ensure that all threads use the same single instance object, you need to lock the class object.
			synchronized (SingletonDoubleCheckDanger.class) {
				if (instance == null) {
					instance = new SingletonDoubleCheckSafe();
				}
			}
		}
		return instance;
	}
}
(6) Holder mode

By defining a static inner class, the < clinit >() method is invoked when the class is initialized to perform static member variable assignment and to execute the code of the static domain. The < clinit > () method is a synchronization method, which can guarantee orderliness, atomicity and visibility. This method is the most widely used.

public final class SingletonHolder {

	private byte[] data = new byte[1024];
	
	private SingletonHolder() {
	}
	
	private static class Holder {
		private static SingletonHolder instance = new SingletonHolder();
	} 
	
	public static SingletonHolder getInstance() {
		return Holder.instance;
	}
}
(7) Enumeration Method

Effective Java highly praises this style of writing, but it is essentially a hungry man's idea.

public enum SingletonEnum {
	
	INSTANCE;
	
	private byte[] data = new byte[1024];
	SingletonEnum() {
		System.out.println(" instance will be initialized immediately.");
	}
	
	public static void method() {
//		Examples of calls
		System.out.println("22222222222222");
	}
	
	public void getOtherMethod() {  
//		Examples of calls
		System.out.println("111111111111111");
    }  
	public static SingletonEnum getInstance() {
		return INSTANCE;
	}
	
	public static void main(String[] args0) {
		SingletonEnum.method();
		SingletonEnum.INSTANCE.getOtherMethod();
	}
}

Tags: socket Java Programming SQL

Posted on Wed, 11 Sep 2019 05:11:25 -0700 by gl_itch