This article has participated in the call for good writing activities, click to view: back end, big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!
1. Introduction
Singletion Pattern is one of the simplest design patterns in Java.
This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created.
This class provides a way to access its unique objects directly, without instantiating the objects of the class.
- A singleton class can have only one instance
- A singleton class must create its own unique instance
- A singleton class must provide this instance to other objects
2. Application scenarios
- Require the production of a unique serial number
- Counters on the WEB, instead of going to the database every time, are cached with singletons
- Creating an object consumes too many resources, such as I/O connections to a database
advantages
- Having only one instance in memory reduces memory overhead, especially the frequent creation and destruction of instances
- Avoid multiple occupancy of resources
disadvantages
- No interface, no inheritance
- You only care about the internal logic, you don’t care about the external how to instantiate
3. Nine implementation methods
3.1 Hungry Chinese V1.0
- When the class is loaded into memory, a singleton is instantiated, and the JVM keeps it thread-safe
- Only downside: Instantiation is done when the class is loaded, whether it is used or not
- Evaluation: Simple and practical, recommended use
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01(a) {}public static Mgr01 getInstance(a) {
return INSTANCE;
}
public static void main(String[] args) { Mgr01 mgr01 = Mgr01.getInstance(); Mgr01 mgr02 = Mgr01.getInstance(); System.out.println(mgr01 == mgr02); }}Copy the code
3.2 Hungry Chinese V2.0
- Hungry 2.0 For Hungry 1.0, the original instantiation is put into a static code block
- Evaluation: The interview can be simply mentioned, in addition to install force, no effect
public class Mgr02 {
private static final Mgr02 INSTANCE;
static {
INSTANCE = new Mgr02();
}
private Mgr02(a) {}public static Mgr02 getInstance(a) {
returnINSTANCE; }}Copy the code
3.3 Slacker V1.0
- I instantiate only when I need to, achieving the purpose of on-demand initialization
- Cons: Does not support multithreading, because it does not lock and does not work in multithreaded state
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03(a) {}public static Mgr03 getInstance(a) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) { System.out.println(Mgr03.getInstance().hashCode()); } }).start(); }}}Copy the code
3.4 Slacker V2.0
- Compared with 1.0, the synchronized keyword guarantee instantiation was added in 2.0.
- Disadvantages: Locking will affect efficiency
public class Mgr04 {
private static Mgr04 INSTANCE;
private Mgr04(a) {}public static synchronized Mgr04 getInstance(a) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr04();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) { System.out.println(Mgr04.getInstance().hashCode()); } }).start(); }}}Copy the code
3.4 Lazy test V3.0
- This is an updated version of the slacker’s test
- Compared with version 2.0, the position of synchronized is mainly optimized
- Disadvantages: When two threads enter the method at the same time andINSTANCENULL also causes thread insecurity
- Cause: An instantiation of Mgr05 is created again when a subsequent thread gains CPU execution rights
- Summary: This test version is useless, just to elicit the DCL, and show the interviewer that you are a thoughtful person during the interview
public class Mgr05 {
private static Mgr05 INSTANCE;
private Mgr05(a){};
public static Mgr05 getInstance(a){
// Trying to improve efficiency by reducing blocks of synchronized code is not feasible
if(INSTANCE == null) {synchronized (Mgr05.class){
try {
Thread.sleep(1);
}catch (Exception e){
e.getStackTrace();
}
INSTANCE = newMgr05(); }}return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) { System.out.println(Mgr05.getInstance().hashCode()); } }).start(); }}}Copy the code
3.5 Two-end check lock (DCL, i.e. :double-checked locking)
- The most important single example, volatile, is essential for the interview
- Why double – end lock check?
- Mainly to solve the above lazy test version of the ineffective issue
- Can’t synchronized be placed directly outside with a null inside?
- It can be, but in general, multiple threads walking to the same line of code at the same time is less of a judgment
- When one of our threads has created an instantiation, we add a judgment to it, and it will screen out subsequent threads without having to fight for the lock
- Why usevolatile ?
- During compilation, Java rearranges unrelated instructions to make the program more efficient
- When objects are created, there are three stages
- Allocate heap memory to INSTANCE
- Call the constructor of Mgr06 to initialize the member variable to form an instance
- Set INSTANCE pointer to allocated memory space (INSTANCE is not null after this step)
- Generally speaking, there is no error in the order of 1-2-3, but instruction rearrangement may lead to 1-3-2. Our object has allocated memory space before initialization of member variables, resulting in serious data errors.
public class Mgr06 {
private volatile static Mgr06 INSTANCE;
private Mgr06(a) {}public static Mgr06 getInstance(a) {
if (INSTANCE == null) {
synchronized (Mgr06.class) {
try {
Thread.sleep(1);
} catch (Exception e) {
e.getStackTrace();
}
if (INSTANCE == null) {
INSTANCE = newMgr06(); }}}return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) { System.out.println(Mgr06.getInstance().hashCode()); } }).start(); }}}Copy the code
3.6 Static inner Classes
- The JVM guarantees singletons that are loaded only once
- The inner class is not loaded when the external class is loaded, so that lazy loading can be realized, and the purpose of loading on demand is truly realized
public class Mgr07 {
private Mgr07(a){}private static class Mgr07Handle{
private static final Mgr07 INSTANCE = new Mgr07();
}
public static Mgr07 getInstance(a){
return Mgr07Handle.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) { System.out.println(Mgr07.getInstance().hashCode()); } }).start(); }}}Copy the code
3.7 the enumeration
- In the interview, you can bring up a few simple things to impress the interviewer
- Source: Effective Java author Josh Bloch advocates this approach
- It not only avoids multithreaded synchronization issues, but also prevents serialization and deserialization
- Enumeration classes have no constructors
- If the class is ENUM, it will throw an exception. Reflection fails
public enum Mgr08 {
INSTANCE;
public Mgr08 getInstance(a){
returnINSTANCE; }}Copy the code
4. To summarize
Bloggers were asked this question in interviews with Xiaomi and Meituan, and the answer is similar to this article. Follow the following procedure to answer the question, so that the interviewer will be impressed
- What is a singleton?
- Evolution of singletons
- Killer DCL double check lock
- Why are there two tests
- Make clear that volatile instruction rearrangement, of course, can be extended directly to visibility, CPU cache lines, depending on the individual
- Finally, mention static inner classes and enumerations
I wish you all a good offer. If you have any questions, please add them to my wechat or leave a message