Java concurrent programming summary 4: final keyword

Obviously I had to make study notes for final keywords before. . . Ok. . . I searched in the local document library for a long time and didn’t find it. Let’s make up and review it~

This piece is mainly for reference: "You listen to __"  Do you think you really understand final?

Learned, well organized, recommended~

Only sort out the key parts, see the original blog post for some details (especially the fourth part and the sixth part)

 

One, final introduction

  • Final can modify variables, methods, and classes to indicate that the modified content will not be changed once assigned. For example, the String class is a final type class. Even if I can know the specific use of final , I think it is easy to ignore the reordering problem of final in multi-threading, and I hope to discuss it together.

Second, the specific use scenarios of final

Final can modify variables, methods and classes, that is, the scope of final use basically covers every place in Java . Here are the positions modified with locks: variables, methods, and classes.

2.1, variables

  • Variables in java can be divided into member variables and method local variables . Therefore, it is also followed this way in order to avoid missing any blind spots.

2.1.1 Final member variables: class variables, instance variables

  • Generally, member variables in each class can be divided into class variables (static modified variables) and instance variables .
  • The timing of assigning initial values to these two types of variables is different . Class variables can be directly assigned initial values ​​when declaring variables or in static code blocks; while instance variables can be assigned when declaring variables. Assign initial values ​​to instance variables, and assign initial values ​​in non-static initialization blocks and in the constructor.
  • Class variables have two occasions to assign initial values , while instance variables can have three occasions to assign initial values . When the final variable is not initialized, the system will not perform implicit initialization, and an error will occur.
  • Class variable : The initial value must be specified in the static initialization block or when the class variable is declared , and it can only be specified in one of these two places;
  • Examples of variables : must be in the non-static initialization block , declare the instance variables or specified initial value constructor , and can only be specified in three places.

2.2.2, final local variables

Final local variables are explicitly initialized by the programmer ;

If the final local variable has been initialized, it cannot be changed again later;

If the final variable is not initialized, it can be assigned. If there is only one assignment , once it is assigned, an error will occur if it is assigned again.

Final basic data types VS final reference data types:

  1. If final modifies the data of a basic data type , once it is assigned, it cannot be changed again ;
  2. For a reference type variable , it only saves a reference, and final only guarantees that the address referenced by the reference type variable will not change , that is, the object is always referenced, but the object properties can be changed .

Macro variable: constant

Using the immutability of final variables, when the following three conditions are met, the variable will become a "macro variable", that is, a constant .

  1. Use final modifier to modify;
  2. The initial value is specified when the final variable is defined;
  3. The initial value can be uniquely specified at compile time.

Note: When the macro variable is used elsewhere in the program, the compiler will directly replace it with the value of the variable.

2.2. Method

Override override:

  1. When the method of the parent class is final modified, the subclass cannot override the method of the parent class;
  2. For example, in Object, the getClass() method is final, we cannot override this method, but the hashCode() method is not modified by final, we can override the hashCode() method.

Overload:

The method modified by final can be overloaded. (Think about it a little bit, it will definitely not be affected)

2.3 Class

When a class is final modified, the table name of the class cannot be inherited by subclasses .

The parent class will be finalized, and when the child class inherits the parent class, an error will be reported.

Since subclass inheritance can often override the methods of the parent class and change the properties of the parent class, it will bring certain security risks. Therefore, when a class does not want to be inherited, you can use final modification.

Three, final example

Final is often used as an immutable class to take advantage of the immutability of final. Let's first take a look at what is an invariant class .

The immutable class means that after an instance of this class is created, the instance variables of the instance cannot be changed . It can become an immutable class if the following conditions are met:

  1. Use private and final modifiers to modify the member variables of the class
  2. Provide a constructor with parameters to initialize the member variables of the class ;
  3. Only provide getter methods for member variables of this class , not setter methods , because ordinary methods cannot modify member variables modified by fina;
  4. If necessary, rewrite the hashCode() and equals() methods of the Object class. You should ensure that equals() is used to determine that the Hashcode values ​​of the same two objects are also equal.

The eight wrapper classes and String classes provided in the JDK are all immutable classes . Let's take a look at the implementation of String:

/** The value is used for character storage. */
 private final char value[];

It can be seen that the value of String is final modified, and the other properties mentioned above are also consistent.

Fourth, the application of final in multithreading

The final use we talked about above should belong to the basic level of Java . After understanding these, do we really have mastered final? Have you considered the concurrency of final in multiple threads?

In the Java memory model , we know that the Java memory model has very few constraints on the bottom layer in order to allow the processor and compiler to give full play to their greatest advantages. That is to say, the Java memory model is a weak memory data model for the bottom layer.

At the same time, the processor and compiler reorder the instruction sequence with the compiler and processor for performance optimization . So, in the case of multi-threading, what kind of reordering of final will be performed? Will it cause thread safety issues? Next, let's take a look at final reordering.

4.1, final domain reordering rules: basic types, reference types

According to the final modified data type classification:

Basic data types:

  1. Final domain writing: It is forbidden to write the final domain and reorder the construction method , that is, it is forbidden to write the final domain to the outside of the construction method, so as to ensure that when the object is visible to all threads, the final domain of the object has been initialized.
  2. Final field reading: It is forbidden to read the reference of the object for the first time and reorder the final field contained in the object .

Reference data type:

An additional constraint is added: it is forbidden to write to the member field of a final modified object in the constructor and subsequently assign the reference of the constructed object to the reference variable to reorder.

Five, the realization principle of final

As we mentioned above, writing the final domain will require the compiler to insert a StoreStore barrier after the final domain is written and before the constructor returns . The reordering rules for reading the final domain will require the compiler to insert a LoadLoad barrier before the operation of reading the final domain .

It is very interesting that if you take X86 processing as an example, X86 will not reorder write-write, so the StoreStore barrier can be omitted . Since operations with indirect dependencies will not be reordered , in the X86 processor, the LoadLoad barrier required to read the final domain will also be omitted . In other words, taking X86 as an example, the memory barrier for read/write of the final domain will be omitted ! Whether it is inserted or not depends on what processor it is.

Guess you like

Origin blog.csdn.net/ScorpC/article/details/113834542