Android single case mode

1. Singleton mode

1. Definition

As the object creation mode, singleton The pattern ensures that there is only one instance of a certain class, and it is self-instantiated and provides this instance to the entire system. This class is called a singleton class.

2. Features

Single case There can only be one instance of a class.

The singleton class must create its own unique instance.

The singleton class must provide this instance to all other objects.

2. Method of creating singleton pattern

1. Lazy man style, thread unsafe

Lazy man style is actually a more vivid title. Since it is lazy, don’t worry when creating object instances. It will wait until the object instance is about to be used before it is created. Lazy people, it will only actually perform work when you can’t get rid of it. Therefore, the object instance is not created when the object is loaded.

public class< span style="color: #000000;"> Singleton {

private static Singleton instance;
private Singleton (){}

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

This code is simple and clear, and uses lazy loading mode, but there are fatal problems. When multiple threads call getInstance() in parallel, multiple instances will be created. That is to say, it cannot work normally under multi-threading.

2. Lazy style, thread Security

In order to solve the above problems, the easiest way is to set the entire getInstance() method to be synchronized.

public static < span style="color: #0000ff;">synchronized Singleton getInstance() {

if (instance == null) {
instance
= new Singleton();
}
return instance;
}

Although it is thread-safe and solves the multi-instance problem, it is not efficient. Because only one thread can call the getInstance() method at any time. But the synchronization operation only needs to be called the first time, that is, the first time the singleton instance object is created. This leads to the double check lock.

3. Double inspection lock

The double checked locking pattern (double checked locking pattern) is a method of locking using synchronized blocks. Programmers call it a double check lock, because there will be two checks instance == null, one is outside the synchronized block, and one is inside the synchronized block. Why do we need to check again in the sync block? Because multiple threads may enter the if outside the synchronized block together, multiple instances will be generated if the second check is not performed in the synchronized block.

public static< span style="color: #000000;"> Singleton getSingleton() {

if (instance == null) {//Single Checked
synchronized (Singleton.class) {
if (instance == null) {//Double Checked
instance = new Singleton();
}
}
}
return instance;
}

This code looks perfect, but unfortunately, it has a problem. The main thing lies in the sentence instance = new Singleton(), which is not an atomic operation. In fact, this sentence probably does the following 3 things in the JVM:

1. Allocate memory for instance

2. Call Singleton’s constructor to initialize member variables

3. Point the instance object to the allocation The memory space (instance will be non-null after executing this step).

But there is an optimization of instruction reordering in the JVM just-in-time compiler. That is to say, the order of the second and third steps above cannot be guaranteed. The final execution order may be 1-2-3 or 1-3-2. If it is the latter, it is preempted by thread two before 3 is executed and 2 is not executed. At this time, instance is already non-null (but not initialized), so thread 2 will directly return to instance, then use it, and then logically Report an error.

We only need to declare the instance variable as volatile.

public class< span style="color: #000000;"> Singleton {

private volatile static Singleton instance; //declared as volatile
private Singleton (){}

public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance
= new Singleton();
}
}
}
return instance;
}
}

Some people think that the reason for using volatile is visibility, that is, it can be guaranteed that the thread will not have a copy of the instance locally, every time Go to main memory to read. But it’s actually wrong. The main reason for using volatile is another feature: it prohibits instruction reordering optimization. In other words, there will be a memory barrier (on the generated assembly code) after the assignment operation of the volatile variable, and the read operation will not be reordered before the memory barrier. For example, in the above example, the fetch operation must be executed after 1-2-3 or 1-3-2, and there is no case that executes to 1-3 and then fetches the value.

From the perspective of the “first generation principle”, the write operation to a volatile variable occurs first in the subsequent read operation of this variable (here, “behind “Is the order of time).

But pay special attention to the use of volatile double check locks in versions prior to Java 5 is still problematic. The reason is that the JMM (Java Memory Model) prior to Java 5 is flawed. Even declaring variables as volatile cannot completely avoid reordering. The main reason is that the code before and after volatile variables still has reordering problems. This volatile shielding reordering problem was fixed in Java 5, so after that, you can use volatile with confidence.

I believe you will not like this complicated and implicitly problematic approach. Of course, we have a better way to achieve thread-safe singleton mode.

4. Hungry Chinese static final field

Hungry Chinese style is actually a more vivid title. Since you are hungry, you are more anxious when you create an object instance. If you are hungry, you will create an object instance when you load the class.

This method is very simple, because the instance of the singleton is declared as static and final variables, and it will be initialized when the class is first loaded into memory, so create The instance itself is thread-safe.

public class< span style="color: #000000;"> Singleton{

//Initialize when the class is loaded
private static final Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return instance;
}
}

The disadvantage is that it is not a lazy initialization mode, and the singleton will start as soon as the class is loaded Is initialized, even if the client does not call the getInstance() method.

The hungry Chinese-style creation method cannot be used in some scenarios: For example, the creation of a Singleton instance depends on parameters or configuration files, and one must be called before getInstance() The method sets the parameters to it, so this singleton method cannot be used.

5. Static inner class static nested class

This method is also recommended on “Effective Java”.

public class< span style="color: #000000;"> Singleton {

private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

This way of writing still uses the JVM’s own mechanism to ensure thread safety. Since the static singleton object is not directly instantiated as a member variable of Singleton, Singleton will not be instantiated when the class is loaded. The internal class SingletonHolder will be loaded when getInstance() is called for the first time. A static type is defined in the internal class Variable INSTANCE, this member variable will be initialized first, and the Java virtual machine will ensure its thread safety, ensuring that the member variable can only be initialized once. Since the getInstance() method does not have any thread locking, its performance will not cause any impact.

Since SingletonHolder is private, there is no way to access it except getInstance(), so it is lazy; it will not be synchronized when reading the instance at the same time, there is no Performance flaws; also does not depend on the JDK version.

6. Enumeration Enum< /strong>

It’s so easy to write singletons with enumerations! This is also its greatest advantage. The following piece of code is the usual way to declare an enumeration instance.

public enum< span style="color: #000000;"> EasySingleton{

INSTANCE;
}

We can access the instance through EasySingleton.INSTANCE, which is much simpler than calling the getInstance() method. Enumeration creation is thread-safe by default, so there is no need to worry about double checked locking, and it can also prevent deserialization from causing new objects to be recreated.

Three. Summary

Generally speaking, there are five ways to write singleton mode: lazy man, hungry man, double check lock, static inner class, and enumeration. The above are all implementations of thread safety. The first method mentioned above is not thread safe, so exclude it.

In general, it’s fine to use the hungry Chinese style directly. If you explicitly require lazy loading (lazy initialization), you tend to use static inner classes. If it involves deserialization and creating an object, you will try to use enumeration to implement a singleton.

Reproduced from: https://lrh1993 .gitbooks.io/android_interview_guide/content/design-mode/Builder-Pattern/Singleton-Pattern.html

public< /span> class Singleton {

private static Singleton instance;
private Singleton (){}

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

public static synchronized Singleton getInstance() {

if (instance == null) {
instance
= new Singleton();
}
return instance;
}

public static Singleton getSingleton() {

if (instance == null) {//Single Checked
synchronized (Singleton.class) {
if (instance == null) {//Double Checked
instance = new Singleton();
}
}
}
return instance;
}

public class Singleton {

private volatile static Singleton instance; //declared as volatile
private Singleton (){}

public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance
= new Singleton();
}
}
}
return instance;
}
}

public class Singleton{

//Initialize when the class is loaded
private static final Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return instance;
}
}

public class Singleton {

private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

public enum EasySingleton{

INSTANCE;
}

Leave a Comment

Your email address will not be published.