Static to realize the hidden trouble of single case

Source: Internet
Author: User

1. Preface

Java's Singleton has several implementations: a simple version of a single thread, a double-check, static, internal class +static, enumeration that cannot work properly under command reordering .... This article is to be discussed in the use of static implementation of the A Hungry man mode of the Singleton, there will be a hidden trouble.

2. The hidden danger of static single case2.1 Traditional notation

The static implementation code for the Singleton is as follows:

< Span class= "Hljs-keyword" >public class Singleton {private Span class= "Hljs-keyword" >static Singleton instance = new Singleton (); private singleton () {} public static Singleton getinstance () {return instance;}} 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

A private class variable, a private constructor, and a factory method to return an instance.

2.2 Hidden Dangers

There is no problem at first glance, but consider a situation when the execution of a constructor depends on a static variable. The code is as follows:

Publicclass Singleton {private static Singleton instance = new Singleton (); private static int i = 1; //1 public static Singleton getinstance () {return instance;} private int count; private singleton () {count = i; //2} public int getCount () {return count; }} 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

In this code, the initial value of the member variable count is given in the constructor, which is related to the static variable i . A slightly Java-based classmate knows that static variables are initialized during class loading, and member variables are initialized when the class is instantiated. So, it's Singleton.getInstance().getCount() supposed 1 to be, so it's logical. However, the fact is often counter-logical: The result of the operation is 0.

2.3 Problem Area

This is clinit caused by the class constructor. The class loading process is divided into several stages (described later), with one called 初始化阶段 . During this phase, a series of actions defined by the programmer are performed, and the operations are put into the clinit method. These actions include the assignment of a static variable (with exceptions, described later), and the code of the static block. The order in which they are organized in Clinit is the order in which they appear in the source code. How does that make sense? See Example:

public class Test{    private static int i = 1; //1 static{ //2 i = 2; }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

To parse the bytecode, you can see clinit the order in which the methods are executed:

If 1, 2 are interchangeable:

public class Test {    static{        i = 2; } private static int i = 1;}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

The byte code changes to:

Back to the question we discussed. When we look at it Singleton clinit , we find:

ibefore assigning a value, the Singleton constructor method is called, and in Singleton the constructor method:

will be assigned i count , but I have not been assigned at this time!

2.4 Solutions

For this example, there is a very simple solution: To add a i final modifier to a constant. iin this way, the assignment is advanced from the initialization phase to the preparation stage (described later).
But this solution is very limited. What if the class loading phase is not just assigning me a value? For example, use static blocks to do more complex operations. At this point, final is powerless. We want to make sure that Singleton cannot be instantiated until the execution of these operations is complete, otherwise it may produce unexpected results.

So, my advice is:

    • All static assignment statements and logical operations are placed into a static block, even if static final (as you will see later, static final is not guaranteed to be assigned during the preparation phase).
    • In the static block, instance the assignment statement is placed at the end.

The code is as follows:

PublicClass Singleton {Privatestatic Singleton instance;Privatestatic int i; //1 static {// All logical operations on static variables are placed in a static block I=1; instance = new Singleton (); The instantiation of span class= "Hljs-comment" >//instance to be placed last} public  Static Singleton getinstance () {return instance;} private int count; private singleton () {count = i; //2} public int getCount () {return count; }} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21st
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21st
  • 22
3. Interesting questions

In this section, we present an interesting question: even static final the modified constants are not guaranteed to be assigned before the constructor.
To understand this problem, first introduce the JVM load class process.

3.1 JVM class loading process


The blue callout portion of the figure is the phase associated with the value of the variable:

    1. In the preparation phase, static variables get memory space in the method area. In addition, those 常量 assignment statements will be executed.
    2. During the initialization phase, the class construction method is executed clinit .
    3. Constructors are executed during object instantiation during the use phase init . That's what we always say 实例化 .
      Knowing the process of class loading and looking back at section 2nd, it is clear that when the initialization phase is not over, the code that is instantiated by the object that uses the stage is executed.
3.2 Interesting questions

As mentioned in section 2nd, if the i final modification is given, the problem can be solved. In fact, this is going to change I to constant, so that i=1 the execution starts from the initialization phase to the prep stage. But this is problematic, and here's a question: Will the static final modified variable be assigned at the prep stage? Let's look at an example:

< Span class= "Hljs-keyword" >public class Singleton {private Span class= "Hljs-keyword" >static final int i = Initi (); private static int initi () {return 1;}}      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

In this example, i the static final initialization code should be executed when it is modified 准备阶段 ? Look at the methods of the class clinit :

I was assigned in the Clinit method, not in the preparation phase. In fact, this is not the case, and when the static final variable is new assigned, it is not 准备阶段 executed. Because the Prep phase only executes: static final decorated, and assignment is the literal value of the assignment statement. This is reflected in the bytecode, that is, the variables in the field attribute table exists Constantvalue, see the following code:

PublicClassSingleton {Privatestatic final int i1 = 1; private static final String str1 =  "1"; private static final int i2 = Initi (); private static final String str2 = new String ( "1"); private static int initi () {return 1;}}       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

View the attribute table for each variable in the bytecode:

Obviously, although it is all static final cosmetic, but i1 str1 because the assignment is literal, so there is a ConstantValue domain, will be assigned in the preparation phase, and i2 str2 one is the return value of the method, one is the object instantiation, so must be in the clinit Method, execute:

4. Summary
      1. Static implementation of the single-meeting is a hidden danger, so the wording to ensure that all initialization in the static block is completed; instance initialization is finished.
      2. Not all static final modified variables are assigned in the preparation phase, which is related to whether the assigned value is literal. To be exact, only variables with constantvalue fields in the compiled attribute table will be assigned in the preparation phase.

Static to realize the hidden trouble of single case

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.