Singletons are arguably the simplest design pattern, requiring that only one object instance be created. The usual way to write this is to declare private constructors that provide static methods to get object instances of singletons.

Common singleton writing is hungry, LanHanShi, double lock validation, and enumeration, a static inner class can, as you know, but according to different writing still have can continue to dig the place, let’s start with the simplest of several kinds of writing reviews singleton, don’t want to see the front of words directly from it.

Review several implementations

The hungry type

Hungry style of writing is usually good static member variable is initialized, advantage is can not lock is access to the object instance, thread safe, the main drawback is that not delay loading, a little memory of waste, because if the initialization logic is relatively complex, such as network request or some complex logic, can produce the waste memory.

LanHanShi

The lazy approach solves the hungry problem of wasting memory by performing initialization only when you really need to fetch instance objects.

Generally speaking, there may be two ways. The first way is to write without locking. Obviously, this is definitely not possible.

However, in the case of the previous JDK version of Synchronized without lock optimization, the performance of obtaining a singleton object each time has a big problem, hence the writing method of DCL.

Double lock validation DCL

In order to solve the lazy performance problem, the double lock verification method was born, which checks once to be empty, performs the lock again if it is empty, and then checks again.

This way, objects are created only when the instance object is empty, the performance problem is solved to a certain extent, and the memory waste problem is not the same as hungry people.

However, there are problems with this method, that is, it will get the incomplete initialization of the object, I also mentioned the problem of this method in a previous article, see a group chat caused by the murder.

Let me just reuse what I wrote here.

From the perspective of CPU, instance = new instance () can be divided into several steps:

  1. Allocates object memory space
  2. The constructor is executed and the object is initialized
  3. Instance refers to the allocated memory address

In fact, steps 2 and 3 May be reordered due to an instruction reordering problem, and the problem occurs.

Instance is first referred to the memory address and then initialized. If another thread accesses the getInstance method, the instance is not null and the object is not fully initialized!

There are a lot of claims that this problem has been fixed in older JDK versions, but I haven’t found any direct evidence of it. If you do, please let me know.

Static inner class

This is a better way to ensure thread-safe and unique creation of singletons through the JVM.

When the Singleton class is loaded, the SingletonHolder is not loaded. The SingletonHolder is initialized only when the getInstance method is called. This not only provides lazy loading, but also uses the JVM class loading mechanism to ensure thread-safe Singleton initialization.

This way is also a relatively recommended way at present.

The enumeration

Implementing singletons through enumerations is the approach advocated by Effective Java author Josh Bloch, and is the best way to implement the singleton pattern.

To see how enumerations implement the singleton pattern, let’s compile the final bytecode generated by the enumeration.

Javap -p singleton.class = javap singleton.class = javap -p singleton.class

To see more detail, we execute javap -c Singleton.

With the resulting bytecode, we actually find that the initialization of the enumeration is essentially done through a static code block.

Consider the steps of class loading: load -> validate -> prepare -> parse -> initialize. The final initialization is the execution of a static code block, which is thread-safe and can only be scheduled by the JVM to ensure thread-safety.

The benefits of enumeration go beyond that. In addition to the obvious simplicity of implementation, enumeration also prevents several problems that other implementations do not avoid.

Let’s talk about a couple of ways

Reflection destruction singleton

In addition to enumerations, there are several ways to destroy singletons by reflection, just to name one implementation, where the final output is false.

If you use reflection to create an enumeration object, an error will be reported. Try it yourself.

Why the error, you can directly look at the source code of newInstance, there is a special section about enumeration type judgment, I marked in red in the following figure.

serialization

In addition to the well-known use of reflection to break singletons, another way to break singletons is serialization.

Implement serialization to the hanhanian method above, and get false with the object changed before and after serialization.

The key is the ois. ReadObject method, which traces the path to the following code:

So obviously what we’ve found is that eventually we’ve actually created a new object by reflection, isInstantiable should actually mean that the class or the property is serialized, and then return true, which is definitely true in our case, so eventually we’ve created a new object.

Why does enumeration prevent this problem? Enumerations are implemented differently, again tracing the implementation logic of the enumeration part.

The red box in the figure below shows the logic for deserializing enumerations. In the end, only the valueOf method is used to find enumerations. There is no logic for creating new objects.

So how do you prevent singletons from being corrupted by serialization in other ways? Looking further down at the source code, the red box indicates that the readResolve method should solve the problem.

In fact, the final solution is as simple as adding methods to a singleton class.

All right, let’s call it a day. It is now 1 o ‘clock in the morning of April 15, Beijing time, sleepy, sleep.