Are enumerating singletons better than DCL and static singletons? That's true~

Let's not talk about the hungry and lazy single examples. DCL and static single examples are briefly introduced to pave the way for the later explanation of enumeration single examples. It's not easy to analyze. Welcome to click three times~

1. Single example of double check lock (DCL)

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton(){
    public static Singleton getInstance(){
        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
        return singleton;

The advantages of this DCL writing method are not only thread safety, but also delayed loading.

1.1 why double check? Can you get rid of the second check?

   of course not. When two threads execute the getInstance method at the same time, the first if judgment will be executed. Due to the existence of the locking mechanism, one thread will enter the synchronization statement first and the other thread will wait. After the first thread executes new Singleton(), it will exit the synchronized protection area. At this time, if there is no second if judgment, Then the second thread will also create an instance, which destroys the singleton.

1.2 why add volatile keyword to singleton?

The main reason is singleton = new Singleton(); Not an atomic operation.

In the JVM, this statement does at least three things

  • 1. Allocate memory space to Singleton instances;
  • 2. Call the constructor of Singleton() to initialize the member field;
  • 3. Point the singleton to the allocated memory space (at this time, the singleton is not null)

Because of the optimization of instruction reordering, the order of steps 2 and 3 cannot be guaranteed. The final execution order may be 1-2-3 or 1-3-2. If the execution order is 1-3-2, let's see what problems will occur

    although the singleton is not null, the space pointed to is not initialized, and an error will still be reported. This is the problem of DCL failure. This problem is difficult to track and reproduce, and may be hidden for a long time.

   the write back regulations from Cache and register to main memory in JMM(Java Memory Model) before JDK1.5 cannot be guaranteed in the order of the second and third above. After JDK 1.5, SUN officially adjusted the JVM to specify the volatile keyword, private static volatile Singleton; As long as volatile is added, it can ensure that it can be read from main memory every time (this involves the consistency of CPU Cache, which is not within the scope of this paper, and is interested in searching by itself). It can also prevent instruction reordering and avoid getting objects that have not completed initialization.

2. Static internal class singleton

public class Singleton{
	private Singleton(){}
	private static class SingletonInstance {
	    private static Singleton singleton = new Singleton();
	public static Singleton getInstance(){
	  return SingletonInstance.singleton;

The difference from the hungry Chinese style is that when the class is loaded, the object will not be instantiated here. The object will be instantiated only by calling the getInstance method.
Like DCL, it has the advantages of delayed loading and high efficiency.

Although DCL and static singleton are good, they do not prevent deserialization and reflection from generating multiple instances. Of course, a better way to write is to enumerate single examples!

3. Enumerate singletons (recommended!!)

All other ways of implementing singletons are actually problematic, that is, they may be destroyed by deserialization and reflection.

Let's take a look at the enumeration class added in JDK 1.5 to implement the singleton

public enum Singleton {

	public void testMethod() {

Advantages of enumeration:

  • 1. The code is concise and elegant without considering lazy loading and thread safety

  • 2. Thread safety

       decompile any enumeration class. You will find that the enumeration items in the enumeration class are defined and initialized through static code blocks (see the analysis of decompile analysis in Section 3.2 below). They will complete initialization when the class is loaded, and the JVM ensures thread safety when loading java classes. Therefore, creating an Enum type enumeration is thread safe

  • Prevent damage to single case

  we know that serialization can write an instance object of a singleton to disk, and then deserialize it to read it back, so as to obtain a new instance. Even if the constructor is private, you can still create a new instance of the class in a special way during deserialization, which is equivalent to calling the constructor of the class.

   Java specifies the serialization of enumeration. During serialization, only the name attribute of the enumerated object is output to the result. During deserialization, you use the valueOf of java.lang.Enum to find an object according to its name instead of creating a new object. Enumeration does not call constructor when serializing and deserializing, which prevents singleton destruction caused by deserialization.

  for reflection destruction singletons, enumeration classes have the same defense measures. When creating objects through newInstance, reflection will check whether this class is an enumeration class. If so, an exception java.lang.illegalargumentexception: cannot create enum objects reflectively, indicating that reflection failed to create objects.

To sum up, enumeration can prevent deserialization and reflection from destroying singletons.

3.1 use of enumeration singleton mode

public enum Singleton {

    public void testMethod() {
        System.out.println("The method of the singleton class is executed");

public class Test {
	public static void main(String[] args) {
        //A singleton class that demonstrates how to use enumeration writing

The operation results are as follows:

3.2 decompile and analyze singleton enumeration classes

    in order to let you know more about enumeration classes, we decompile javap -p Singleton.class by enumerating singleton classes above, where - p means that private methods should be included during decompilation.

// This is the decompiled content
public final class Singleton extends java.lang.Enum<Singleton> {
  public static final Singleton INSTANCE;
  private static final Singleton[] $VALUES;
  public static Singleton[] values();
  public static Singleton valueOf(java.lang.String);
  private Singleton();
  public void testMethod();
  static {};

As we can see,

  • INSTANCE is an INSTANCE of the Singleton class
  • Singleton inherits the java.lang.Enum class
  • There is also a private Singleton parameterless constructor. The enumeration items of the enumeration class will be instantiated using this constructor, that is, the INSTANCE here will be instantiated using this constructor.
  • The instantiation process takes place in the last empty static code block. You can further analyze the bytecode content in static through other parameters of javap. Static actually contains many bytecode instructions. These instructions are used to initialize the enumeration item INSTANCE, and the static code block is executed when the class is loaded, that is, when the Singleton class is loaded, INSTANCE is initialized. In the static code block, in addition to initializing INSTANCE, the private array defined by Singleton[] $VALUES is also created and initialized in static. Then put all enumeration items into the $values array in the defined order. Finally, we can access the array through the values method

   in order to analyze the operations in each method, we use javap -p -c -v Singleton.class to see more details, - c to see the bytecode in each method, and - v to print out the constant pool information. You can understand it here. If you don't understand it, you can see my above conclusion. The key point is to look at the bytecode in the static code block. The following is to verify the conclusion.

 * @author: Brick ocean__
 * @description: I'll focus on the last static part
public final class Singleton extends java.lang.Enum<Singleton>
  minor version: 0
  major version: 52
Constant pool:					// You need to pay attention to the part of constant pool. You can go back to it later when analyzing each instruction
   #1 = Fieldref           #4.#37         // Singleton.$VALUES:[LSingleton;
   #2 = Methodref          #38.#39        // "[LSingleton;".clone:()Ljava/lang/Object;
   #3 = Class              #17            // "[LSingleton;"
   #4 = Class              #40            // Singleton
   #5 = Methodref          #13.#41        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #13.#42        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = Fieldref           #43.#44        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = String             #45 / / the method of the singleton class is executed
   #9 = Methodref          #46.#47        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = String             #14            // INSTANCE
  #11 = Methodref          #4.#42         // Singleton."<init>":(Ljava/lang/String;I)V
  #12 = Fieldref           #4.#48         // Singleton.INSTANCE:LSingleton;
  #13 = Class              #49            // java/lang/Enum
  #14 = Utf8               INSTANCE
  #15 = Utf8               LSingleton;
  #16 = Utf8               $VALUES
  #17 = Utf8               [LSingleton;
  #18 = Utf8               values
  #19 = Utf8               ()[LSingleton;
  #20 = Utf8               Code
  #21 = Utf8               LineNumberTable
  #22 = Utf8               valueOf
  #23 = Utf8               (Ljava/lang/String;)LSingleton;
  #24 = Utf8               LocalVariableTable
  #25 = Utf8               name
  #26 = Utf8               Ljava/lang/String;
  #27 = Utf8               <init>
  #28 = Utf8               (Ljava/lang/String;I)V
  #29 = Utf8               this
  #30 = Utf8               Signature
  #31 = Utf8               ()V
  #32 = Utf8               testMethod
  #33 = Utf8               <clinit>
  #34 = Utf8               Ljava/lang/Enum<LSingleton;>;
  #35 = Utf8               SourceFile
  #36 = Utf8     
  #37 = NameAndType        #16:#17        // $VALUES:[LSingleton;
  #38 = Class              #17            // "[LSingleton;"
  #39 = NameAndType        #50:#51        // clone:()Ljava/lang/Object;
  #40 = Utf8               Singleton
  #41 = NameAndType        #22:#52        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #42 = NameAndType        #27:#28        // "<init>":(Ljava/lang/String;I)V
  #43 = Class              #53            // java/lang/System
  #44 = NameAndType        #54:#55        // out:Ljava/io/PrintStream;
  #45 = Utf8 executes the method of the singleton class
  #46 = Class              #56            // java/io/PrintStream
  #47 = NameAndType        #57:#58        // println:(Ljava/lang/String;)V
  #48 = NameAndType        #14:#15        // INSTANCE:LSingleton;
  #49 = Utf8               java/lang/Enum
  #50 = Utf8               clone
  #51 = Utf8               ()Ljava/lang/Object;
  #52 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #53 = Utf8               java/lang/System
  #54 = Utf8               out
  #55 = Utf8               Ljava/io/PrintStream;
  #56 = Utf8               java/io/PrintStream
  #57 = Utf8               println
  #58 = Utf8               (Ljava/lang/String;)V
  public static final Singleton INSTANCE; // Define enumeration items
    descriptor: LSingleton;

  private static final Singleton[] $VALUES; // Define array
    descriptor: [LSingleton;

  public static Singleton[] values();
    descriptor: ()[LSingleton;
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[LSingleton;
         3: invokevirtual #2                  // Method "[LSingleton;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[LSingleton;"
         9: areturn
        line 1: 0

  public static Singleton valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)LSingleton;
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class Singleton
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class Singleton
         9: areturn
        line 1: 0
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;

  private Singleton();
    descriptor: (Ljava/lang/String;I)V
    flags: ACC_PRIVATE
      stack=3, locals=3, args_size=3
         0: aload_0							// The stack operation instruction loads the variable load at position 0 in the local method table onto the stack. The a prefix indicates that it is a reference type.
       // Reminder: when the JVM executes a piece of code, it will first store all the variables used in a local variable table - local variable table.
       // When calculating on the stack, you need to use the values of the local method table, and they will be loaded onto the stack through the load instruction
       // After the operation on the stack, the value needs to be saved back to the local method table, so there will also be a corresponding store instruction, and load and store correspond.
         1: aload_1
         2: iload_2
         3: invokespecial #6                  // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
         6: return
        line 1: 0
        Start  Length  Slot  Name   Signature
            0       7     0  this   LSingleton;
    Signature: #31                          // ()V

  public void testMethod();
    descriptor: ()V
    flags: ACC_PUBLIC
      stack=2, locals=1, args_size=1
         0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #8 / / String executes the method of the singleton class
         5: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
        line 5: 0
        line 6: 8
        Start  Length  Slot  Name   Signature
            0       9     0  this   LSingleton;

  static {};
    descriptor: ()V.  						// Is to return the void type
    flags: ACC_STATIC
      stack=4, locals=0, args_size=0
         0: new           #4                  // class Singleton
         // new #4 means to get the name of the type labeled 4 from the Constant pool. Looking up at the definition of the Constant pool part, we can see that it is the Singleton class, and then new comes out and becomes an object
         3: dup								  // Then dup stack
         4: ldc           #10 / / String INSTANCE: push the value INSTANCE of string type labeled 10 in the constant pool to the top of the stack
         6: iconst_0						  // Define a variable of type int with a value of 0. I don't know what use it is to define a constant here
         7: invokespecial #11 / / method "< init >": (ljava / Lang / string; I) V, call the constructor to initialize, and the return type is void
        10: putstatic     #12                 // Field INSTANCE:LSingleton; Assign a value to the static variable instance, which is the same as name
        13: iconst_1						  // Define a variable of type int with a value of 1, then merge it
        14: anewarray     #4 / / class Singleton, create an array containing Singleton enumeration types, here is the $VALUES array
        17: dup
        18: iconst_0
        19: getstatic     #12                 // Field INSTANCE:LSingleton; Get the name value of the field instance
        22: aastore							  
        23: putstatic     #1 / / field $VALUES: [lsingleton; put the name VALUES of enumeration items in the local variable table into the $VALUES array in turn
        26: return
        line 2: 0
        line 1: 13

   in fact, the compiled bytecode is for the JVM. The JVM only needs to execute in a mindless order. Many aspects involve a lot of contents of the JVM, which is irrelevant to the topic of this article. A separate article on bytecode instructions will be opened later. Here we mainly look at the instructions in the static code block and understand why enumeration is thread safe.

Welcome to triple CLICK~

If you have any questions, please leave a message. Let's discuss and study together

----------------------Talk is cheap, show me the code-----------------------

Tags: Java Design Pattern Singleton pattern

Posted on Sun, 31 Oct 2021 17:44:51 -0400 by Simmo