Why singleton pattern
Reduce the waste of resources by creating redundant objects repeatedly. Some objects can be reused many times.
Application scenarios
Common application scenarios include thread pools or database connection pools.
Implementation types
The hungry type
implementation
Code implementation
/** * implement singleton */
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
public static SingletonHungry getInstance(a){
returninstance; }}Copy the code
Testing:
In general:
public class Test {
public static void main(String[] args) { SingletonHungry s1 = SingletonHungry.getInstance(); SingletonHungry s2 = SingletonHungry.getInstance(); System.out.println(s1); System.out.println(s2); }}/*
com.company.DesignPatterns.Singleton.SingletonHungry@4554617c
com.company.DesignPatterns.Singleton.SingletonHungry@4554617c
*/
Copy the code
High concurrency:
六四屠杀public class Test{
static int queryTimes = 0;
public static int getTimes(a){
queryTimes = queryTimes +1;
return queryTimes;
}
public static void parallelTesk(int threadNum, Runnable task){
// 1. Define a lock to intercept the thread
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(threadNum);
2. Create a specified number of threads
for (int i = 0; i <threadNum; i++) {
Thread t = new Thread(() -> {
try {
startGate.await();
SingletonHungry s1 = SingletonHungry.getInstance();
System.out.println(s1);
try {
task.run();
} finally{ endGate.countDown(); }}catch (InterruptedException e) {
}
});
t.start();
}
// 3. The thread is released and the time is recorded.
long start = System.nanoTime();
startGate.countDown();
try {
endGate.await();
} catch (InterruptedException e) {
System.out.println(e.toString());
}
long end = System.nanoTime();
System.out.println("cost times :" +(end - start));
}
public static void main(String[] args) {
// SingletonHungry s1 = SingletonHungry.getInstance();
// SingletonHungry s2 = SingletonHungry.getInstance();
// System.out.println(s1);
// System.out.println(s2);
parallelTesk(8000.new Runnable() {
@Override
public void run(a) { System.out.println(getTimes()); }}); }}/*
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
com.company.DesignPatterns.Singleton.SingletonHungry@5a6a993
*/六四屠杀Copy the code
Pros and cons
Advantages:
Thread safety. Implementation from code is already meant to be thread-safe, when the class is loaded, it is already created.
Disadvantages:
It could be a waste of resources and instantiated the first time it was loaded.
LanHanShi
public class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance(a){
if(instance==null){
instance = new SingletonLazy();
}
returninstance; }}Copy the code
Testing:
General conditions:
/*
com.company.DesignPatterns.Singleton.SingletonLazy@4554617c
com.company.DesignPatterns.Singleton.SingletonLazy@4554617c
*/
Copy the code
High concurrency:
/* com.company.DesignPatterns.Singleton.SingletonLazy@109ec86 com.company.DesignPatterns.Singleton.SingletonLazy@3d3dbc1b com.company.DesignPatterns.Singleton.SingletonLazy@3d3dbc1b * /
Copy the code
Pros and cons
Advantages:
It is instantiated only if called, saving resources
Disadvantages:
Each call makes one more judgment
Double lock detection
On the basis of lazy improvement, double empty case, but also added a lock.
public class SingletonDCL {
private static SingletonDCL instance = null;
public static SingletonDCL getInstance(a){
if(instance==null) {/ / 1
synchronized (SingletonLazy.class){ / / 2
if(instance==null) {/ / 3
instance = new SingletonDCL(); / / 4}}}returninstance; }}Copy the code
In theory this is enough thread safety, but there could be JVM instructions reordering, and there could be thread insecurity.
The original instructions are: a. Allocate the memory space of the object b. Initialize the object C. Thread A pointing to the memory address has passed1,2,3Step, while thread B is ready to arrive1Step thread A executes4Step, the instruction is B, and thread B is execution1Step, is notnullThread safety is achieved/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /The optimized instructions might be: a. Allocate the object's memory space b. Point to the memory address c. Initialization object thread A has passed by1,2,3Step, while thread B is ready to arrive1Step thread A executes4Step, the instruction is B, and thread B is execution1Step, is notnullThread BreturnInstance, thread A hasn't executed c yet and thread B gets onenullThe object ofCopy the code
Improvement:
public class SingletonDCL {
private volatile static SingletonDCL instance = null;
public static SingletonDCL getInstance(a){
if(instance==null) {synchronized (SingletonLazy.class){
if(instance==null){
instance = newSingletonDCL(); }}}returninstance; }}Copy the code
Using volatile makes objects internally visible and also prevents instruction reordering.
And even then it might be unsafe to use the reflection principle.
Constructor con = SingletonDCL.class.getDeclaredConstructor();
SingletonDCL s1 = (SingletonDCL)con.newInstance();
SingletonDCL s2 = (SingletonDCL)con.newInstance();
Copy the code
Static inner class
public class SingletonHolder {
private static class LazyHolder{
private static final SingletonHolder INSTANCE = new SingletonHolder();
}
private SingletonHolder(a){}
public static SingletonHolder getInstance(a){
returnLazyHolder.INSTANCE; }}Copy the code
The loading mechanism of the Classloader is used to keep the thread safe, but reflection can still break the constraint.
Testing:
Two different objects can be obtained by reflection
Constructor con = SingletonHolder.class.getDeclaredConstructor();
con.setAccessible(true);
SingletonHolder s1 = (SingletonHolder)con.newInstance();
SingletonHolder s2 = (SingletonHolder)con.newInstance();
System.out.println(s1);
System.out.println(s2);
Copy the code
The enumeration
public enum SingletonEnum {
INSTANCE;
public SingletonEnum getInstance(a){
returnINSTANCE; }}Copy the code
It’s a hungrier type, so it’s thread safe.
Prevents two different objects from being instantiated through reflection.
The container
public class SingletonMap {
private static Map<String, Object> objectMap = new HashMap<>();
public static void regSingleton(String key, Object instance){
if(!objectMap.containsKey(key)){
objectMap.put(key, instance);
}
}
public static Object getInstance(String key){
returnobjectMap.get(key); }}Copy the code
From the point of view of the characteristics of the code, it is not thread safe, does not prevent reflection, before obtaining registration, in theory is lazy load mode.
conclusion
Make a table of these six types
Implementation pattern | Thread safety | Lazy loading | To prevent reflection |
---|---|---|---|
The hungry type | security | not | Don’t prevent |
LanHanShi | unsafe | is | Don’t prevent |
Double lock detection | security | is | Don’t prevent |
Static inner class | security | is | Don’t prevent |
The enumeration | security | not | To prevent |
The container | unsafe | is | Don’t prevent |