summary
Singleton mode: in the whole system, only one instance object of a class can be obtained and used
Key points:
- Private constructor
- Create object inside class
- Provide static methods for obtaining instances externally
Hungry Han style
Common way
Hungry Chinese style: when the class is loaded, create the object directly, which is thread safe
public class Singleton { private static Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance() { return INSTANCE; } }
Possible problems:
- It may cause a waste of memory
- It is not safe for reflection and deserialization
Enumerative
public enum Singleton { INSTANCE { @Override protected void method() { ... } } protected abstract void method(); }
public void test() { Singleton s = Singleton.INSTANCE; }
It is safe for reflection and deserialization. Because it has no constructor, getting the constructor through reflection to create an object will throw an exception
Lazy style
Non thread safe
Lazy: load the object the first time you use it. Thread safety issues exist
Instead of creating objects when class 1 is loaded, there is randomness in concurrency
public class Singleton { private static Singleton INSTANCE = null; private Singleton() { } public static Singleton getInstance() { if (INSTANCE == null) { INSTANCE = new Singleton(); } return INSTANCE; } }
synchronized
Use synchronized to achieve thread safe lazy. Low efficiency, improvement: double check lock
public class Singleton { private static Singleton INSTANCE = null; private Singleton() { } public static synchronized Singleton getInstance() { if (INSTANCE == null) { INSTANCE = new Singleton(); } return INSTANCE; } }
public class Singleton { private static Singleton INSTANCE = null; private Singleton() { } public static Singleton getInstance() { synchronized (Singleton.class) { if (INSTANCE == null) { INSTANCE = new Singleton(); } } return INSTANCE; } }
Double check lock
public class Singleton { private static volatile Singleton INSTANCE = null; private Singleton() { } public static Singleton getInstance() { if (INSTANCE == null) { synchronized (Singleton.class) { if (INSTANCE == null) { INSTANCE = new Singleton(); } } } return INSTANCE; } }
Reduce the number of synchronizations. However, it is not 100% safe because there is instruction rearrangement (which may cause a thread to get an empty object). Using volatile prohibits instruction rearrangement
ThreadLocal
A space for time operation cannot guarantee the safety of multithreading
public class Singleton { private static Singleton INSTANCE = null; private Singleton() { } private static final ThreadLocal<Singleton> threadLocalSingleton = new ThreadLocal<Singleton>() { @Override protected Singleton initialValue() { return new Singleton(); } } public static Singleton getInstance() { return threadLocalSingleton.get(); } }
CAS
Lock free, optimistic strategy, thread safety
Disadvantages: many garbage objects will be generated, and may cause an endless loop
public class Singleton { private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>(); private Singleton() { } public static final Singleton getInstance() { for (;;) { Singleton current = INSTANCE.get(); if (current != null) return current; if (INSTANCE.compareAndSet(null, )) { return current; } } } }
The difference between hungry and lazy
- The creation time is different. The hungry type creates an object instance when the class is loaded, and the lazy type creates it when it is used
- There is no thread safety problem for the hungry type, and there is thread safety problem for the lazy type
- There is the possibility of wasting resources
- java.lang.Runtime is a classic singleton pattern