Author: the summer solstice welcome to reprint, please keep this statement blog.csdn.net/u011418943/…

In the last article, we talked about the policy pattern, which is needed for changing code. If you are interested, you can check it out. Portal: One of Android’s most common design patterns — Strategy

The definition of singleton pattern is not explained too much, I believe that many partners in the design, are using this pattern; Common scenarios are database access, file stream access and network connection pool access, etc. In these scenarios, we all want to have only one instance, in addition to reducing memory overhead, but also to prevent the multi-process modification file disorder and database lock problems. In this article, I’ll take you through the common centralized singleton patterns on Android and analyze their strengths and weaknesses in detail. Let you in the future select singleton, can choose according to the actual situation. Of course, you are welcome to correct any mistakes. Here is the introduction:

1. Hungry

When you initialize it, you initialize it directly, typical time for space.

Private SingleMode(){}; public class SingleMode{// Private SingleMode(){}; Private static SingleMode instance = new SingleMode(); Public static SingleMode getInstance(){return instance; }}Copy the code

The benefits of the hungry type is thread-safe, because virtual machine guarantee will only load time, load classes again, will not concurrently, thus guarantee the thread safety problem. But the disadvantage is also obvious, as soon as I initialize the instance takes up memory, but I don’t want to use my pants.

2. Slacker style

In order to solve the above problem, open the lazy style, is the need to use, to load;

Private SingleMode(){}; public class SingleMode{// Private SingleMode(){}; private static SingleMode mSingleMode; Public static singleModeInstance (){if (mSingleMode == null){mSingleMode = new SingleMode(); } return mSingleMode; }}Copy the code

Slob as shown above, advantages:

We only need to allocate memory when we need it, and we can get parameters from outside and instantiate them. This is the biggest advantage of lazy style

Disadvantages:

A single thread is only instantiated once. If it is multi-threaded, it will be instantiated multiple times

What is it that says it is thread unsafe? Here’s the picture:

Let’s say that there are two threads, A and B, that want to initialize this instance; At this time, A is fast, and has judged that mSingleMode is null and is creating instances, while B will also judge at this time, but A has not finished new at this time, so mSingleMode is still empty, so B also starts to new an object. This is equivalent to creating two instances, so the above design is not thread-safe.

2.1. How to achieve lazy thread safety?

Some people say, “Easy, since you are a thread concurrency is not safe, so add a synchronized thread lock and you are done.” But that slows down the overall access speed, and every time you have to decide, is this really what we want?

Because of the above disadvantages, we can add an optimization to the above lazy style, such as double check shackles:

Private SingleMode(){}; public class SingleMode{// Private SingleMode(){}; private static SingleMode mSingleMode; public static SingleMode getInstance(){ if (mSingleMode == null){ synchronized (SingleMode.class){ if (mSingleMode == Null){// mSingleMode = new SingleMode(); } } } return mSingleMode; }}Copy the code

On the basis of the above, a secondary check is used to ensure thread safety. It will first determine whether null is used before loading, and synchronized modification ensures thread safety.

However, if we did not use volatile above, it would still be unsafe and null could be a problem. Why is that? This is because when Java new an object, it is unordered. MSingleMode = new SingleMode(); mSingleMode = new SingleMode(); It doesn’t happen overnight. It takes 3 steps.

  • 1. Create memory for mSingleMode
  • 2. New SingleMode() calls this constructor
  • 3. MSingleMode points to the memory region

Well, you might wonder, isn’t that normal? How can there be null? No, the Java virtual machine does not perform the above three steps in this order, and may be scrambled. Here is the Java reorder, such as 2 and 3 switch:

  • 1. Create memory for mSingleMode
  • 3. MSingleMode points to the memory region
  • 2. New SingleMode() calls this constructor

So at this point, mSingleMode is already pointing to the memory region, so at this point it’s not null, and it doesn’t actually get a constructor, like there’s some parameters or methods in the constructor, but you don’t get them, and then thread B comes in, MSingleMode already points to a memory region that is not empty, but the methods and parameters are not retrieved, so you will get an error if you execute some method of mSingleMode.

This is very rare, of course, but it does expose the problem. So we use volatile. We know that an important property of volatile is visibility. That is, objects that are volatile can be updated in real time between threads. But it also has the effect of disabling Java reordering so that we don’t have to worry about null situations like the one above. As follows:

Private SingleMode(){}; public class SingleMode{// Private SingleMode(){}; private volatile static SingleMode mSingleMode; public static SingleMode getInstance(){ if (mSingleMode == null){ synchronized (SingleMode.class){ if (mSingleMode == Null){// mSingleMode = new SingleMode(); } } } return mSingleMode; }}Copy the code

See here, does not feel like climbing hundreds of pit, finally on the golden stage… However, it’s not that you’ve been ranked and you’ve still been suspended, so maybe we’re missing something. Yes, there is still a downside to this approach: It is not very efficient because the volatile keyword blocks necessary code optimizations in the virtual machine. Therefore, it is recommended not to use it in large quantities without special needs.

For some reason, when I entered the activity for the second time, the view didn’t come out, but the data object and everything else were there. I was so exhausted that I switched to another singleton mode and was ok. .

If you say that, how can you play? Is there a better way? Don’t worry, look down.

3, static

What is static? To review the above example, we initialize classes at the very beginning, whether you need it or not, and we also said that Java does not load classes concurrently, so can we use lazy load, that is, initialization when needed, and keep the thread safe? Of course, here it is:

Private SingleMode(){}; public class SingleMode{// Private SingleMode(){}; public static class Holder{ private static SingleMode mSingleMode = new SingleMode(); public static SingleMode getInstance(){ return mSingleMode; }}}Copy the code

In addition to the hungry and lazy style above, static has the advantage of being thread-safe and not having to worry too much about it, but has the disadvantage of poor parameter passing. So at this point, the question is, how do you pass parameters? It’s not as convenient as lazy, but it doesn’t matter, we can just define init (), but we have one more line of code to initialize it; Such as:

Private SingleMode(){}; public class SingleMode {// Private SingleMode(){}; public static class Holder{ private static SingleMode mSingleMode = new SingleMode(); public static SingleMode getInstance(){ return mSingleMode; } } private Context mContext; public void init(Context context){ this.mContext = context; }}Copy the code

Initialization:

 SingleMode mSingleMode = SingleMode.Holder.getInstance();
 mSingleMode.init(this);Copy the code

4. Enumerate singletons

Before Java 1.4, we used to implement the singleton pattern in the form of static inner classes, but since 1.5, the idea has been raised in Effective Java. The advantages of using enumerations are as follows:

  • Thread safety
  • Lazy loading
  • Serialization and deserialization security

As a result, singletons are now generally implemented as a single enumeration, as above. Let’s change this:

public static SingleMode getInstance(){ return Singleton.SINGLETON.getSingleTon(); } public enum Singleton{ SINGLETON ; Private AppUninstallModel Singleton; private AppUninstallModel singleton; Singleton(){//JVM guarantees only one instance Singleton = new AppUninstallModel(); } public SingleMode getSingleTon(){return singleton; }}Copy the code

Okay, so that’s ok, but again, initialization arguments are just like static classes, so you have to write init() again.

So our singleton pattern is done.