As we all know, the adoption of reasonable design pattern in the code can not only make the code easier to be understood by others, but also make the overall module have a more reasonable structure, which is convenient for later expansion and maintenance. This leads to “patterns” that we call “design patterns”.

In addition, if you want to understand some knowledge, you must be clear about the order, that is, what problems you have encountered, how to solve them and whether there is a better way to think about them, so that you can achieve twice the result with half the effort.

Let’s get back to the singleton pattern. In accordance with the above order of thought, let’s analyze step by step.

1. If there is a present, there is no present

When we use an instance of a class, we simply use the keyword new to create an instance object. But sometimes you might use an instance object a lot, or it might be expensive to create, for example, if you have a butler and you need a butler to do something for you, you can’t hire a new one every time you need a butler, right? The best way to do this is to keep the housekeeper on a permanent basis, and just call her when you need her. Suddenly I found that this example is very appropriate ah!

From the above, we run into the problem of creating an instance object of a class that is frequently used, or that takes a lot of time and effort to create, and we want to create only one object once, and only one is enough (if you have to hire two butlers, I can only say you have the money). In this case, let’s start thinking about how to solve this problem.

2. Make suggestions and let a hundred flowers bloom

Everyone began to offer good ideas, one by one, first, Bachelor Zhao you speak ~

2.1 the hungry

In fact, very good solution, look at my following code:

Public class Singleton {private static Singleton sInstance = new Singleton(); privateSingleton() {
    }

    public static Singleton getInstance() {
        returnsInstance; }}Copy the code

Privatize the constructor and I don’t want to explain that, so you can’t just create objects externally using the new keyword; The static member variable sIntance is initialized when the Singleton class is loaded. The Singleton object is created and only one Singleton object exists. The singleton.getInstance () method is used to get the instance object, which is the Singleton pattern! That solves the problem, people!

Nevertheless money minister thought to want to say, nevertheless this kind of method seems to have a bit malpractice, if I still don’t need housekeeper now, total can’t let my white money keep? Can I pay for a butler when I need it?

Ah ~~ you say so also have reason, that money minister, say your method.

2.2 LanHanShi

Without further ado, let’s look at the code:

Public class Singleton {private static Singleton sInstance; privateSingleton() {
    }

    public static Singleton getInstance() {
        if (sInstance == null) {
            sInstance = new Singleton();
        }
        returnsInstance; }}Copy the code

How’s that? This method is not bad! Member variables are initialized by default and do not create objects. When singleton.getInstance () is called, if sInstance is null then the object is created.

At this time prime Minister Sun “hum” once said, you are not as good as Zhao Bachelor! Zhao may have hired a butler for free in advance, and you may have hired several more. You don’t even consider multithreading! Minister of money a listen to hurriedly made the modification, the code is as follows:

Public class Singleton {private static Singleton sInstance; privateSingleton() {
    }

    public synchronized static Singleton getInstance() {
        if (sInstance == null) {
            sInstance = new Singleton();
        }
        returnsInstance; }}Copy the code

Synchronized (synchronized); synchronized (synchronized); synchronized (synchronized); Prime minister Sun again “hum” 1, but every time call will be because of this lock to bring time overhead, you think unlock don’t time ah? Poor performance! The minister blushed a little, so he revised it:

Public class Singleton {private static Singleton sInstance; privateSingleton() {
    }

    public static Singleton getInstance() {
        if(sInstance == null) { synchronized (Singleton.class) { sInstance = new Singleton(); }}returnsInstance; }}Copy the code

SInstance = new Singleton(); Lock statement, should be ok? Sun prime minister “hum” for the third time, I give you assume that A situation, set the existing thread A and B, at some point the two threads are passed to an empty statement but did not take to lock resources, and then thread A first obtained lock resources into the critical region code block (locked), created an object, and then exit the critical section, releases the lock resources. Thread B acquires the lock resource, enters the critical section, creates the object, exits the critical section, and releases the lock resource. How many Sinleton objects are there? Minister money after listening to say that I directly add the lock to empty sentence before!

Public class Singleton {private static Singleton sInstance; privateSingleton() {
    }

    public static Singleton getInstance() {
        synchronized (Singleton.class) {
            if(sInstance == null) { sInstance = new Singleton(); }}returnsInstance; }}Copy the code

Prime Minister Sun laughed and said, “What’s the difference between that and synchronized?” “You can you up, no can no bb!

2.3 Double Checked Locking (DCL)

Prime Minister Sun waved his big hand and said, “Watch it! Let me teach you how to be a man today!”

Public class Singleton {private static volatile Singleton sInstance; privateSingleton() {
    }

    public static Singleton getInstance() {
        if (sInstance == null) {
            synchronized (Singleton.class) {
                if(sInstance == null) { sInstance = new Singleton(); }}}returnsInstance; }}Copy the code

First, method locks are changed to code block locks to reduce the scope of locks. The second and first empty call improves efficiency in the case of single thread, but if there are two concurrent threads at the same time, that is, both empty call is successful, and then the second empty call in the lock will be filtered. Again, let’s say we have threads A and B, and at some point both threads pass the first null-statement but neither thread gets the lock. Then thread A obtains the lock resource to enter the critical area (the locked code block), executes the second null-detection statement, null-detection succeeds, creates an object, and exits the critical area to release the lock resource. Then thread B obtains the lock resource and enters the critical area, executes the null statement and finds that it does not pass, directly exits the critical area and releases the lock resource.

The addition of the volatile keyword to the member variable sInstance is particularly important. Let me be coy: in the Java Memory model (JMM), there is no limit to the instruction order of the processor, which means that the order can be scrambled without affecting the results.

SInstance = new Singleton(); The JMM does not execute this command immediately, i.e., atomically. The command is essentially divided into three parts:

  1. Allocates memory for objects
  2. Execute the constructor statement to initialize the instance object
  3. Reference sInstance to the allocated memory space

In JMM, the steps 2 and 3 are not necessarily executed in sequence. If thread A executes in sequence 1, 3, and 2, and thread B executes its first nullated statement at the end of step 2, sInstance will be returned directly, so the sInstance obtained at this time is just not NULL. With virtually no initialization, such an object must be problematic!

The purpose of the volatile keyword is to ensure that the command cannot be reordered, so this method of obtaining singletons is truly safe and reliable.

Out of the blue, general Li, who had been silent, said, “Prime Minister Sun, don’t you think it’s troublesome to write like this?” I have an easier way to write it!

2.4 Singleton pattern for static inner class implementation

A static inner class can not only ensure the existence of a singleton, but also ensure thread safety. The code is as follows:

Public class Singleton {privateSingleton() {
    }

    public static Singleton getInstance() {
        returnSingletonHolder.sInstance; } private static class SingletonHolder { private static Singleton sInstance = new Singleton(); }}Copy the code

When the external Singleton class is loaded, its static inner SingeletonHolder class is not loaded, so its member variable sInstance is not initialized. Only when the singleton.getInstance () method is called, The SingeletonHolder is loaded and its member variables are initialized, while the class is thread-safe. This ensures lazy loading and thread-safe loading, and simplifies code.

2.5 Enumerating singletons

After saying the above four singleton pattern implementation, I don’t know if you have thought of a problem, that is serialization. We can write an instance to disk and then read it from disk with the following code. Even if the constructor is private, deserialization has a special way of recreating a new instance:

public Singleton createNewInstance() throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); // Singleton is oos. WriteObject (singleton); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); // A new deserialized instance object is returnedreturn (Singleton) ois.readObject();
}
Copy the code

As you can see from the above code, deserialization gives you a new instance object, so can this phenomenon be avoided? It can be avoided. Deserialization provides a special method, readResolve(), a private, instantiated method that gives developers control over deserialization of objects. To prevent this, add the readResolve() method to the singleton pattern as follows:

private Object readResolve() {// The instance object in the singleton pattern is returned herereturn sInstance;
}
Copy the code

In Effective Java, author Joshua Bloch advocates an enumeration approach to solving all of these problems:

Public enum SingletonEnum {INSTANCE; public voidmethod() {/ /dosomething... }}Copy the code

You can get a singleton through singletonene.instance and then call various internal methods. Enumeration implementation singletons have the following benefits:

  1. Instance creation is thread-safe, ensuring singletons;
  2. Prevents multiple instances from being created by reflection;
  3. No serialization issues.

Although this approach has not been widely adopted, enumerated types of single elements have emerged as the best way to implement the Singleton Singleton pattern.

3. Summary

Through the above step by step analysis, I do not know whether you have a new understanding of singleton pattern? In general, the singleton pattern with volatile double check locks and static inner class implementations is by far the most widely used, and enumerating singletons is a better way to get singletons if you’re more demanding. Welcome you can exchange more, point out deficiencies, common learning progress!