Multithreading tutorial immutable design

Posted by Brandon_R on Mon, 28 Feb 2022 07:32:32 +0100

Multithreading tutorial (XXIX) immutable design

Common invisibility designs include time formats and string s

Take string as an example to illustrate the elements of immutable design

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    /** Cache the hash code for the string */
    private int hash; // Default to 0

    // ...

}

1. Use of final

It is found that the class and all attributes in the class are final

  • The attribute is decorated with final to ensure that the attribute is read-only and cannot be modified
  • Class is decorated with final to ensure that the methods in this class cannot be overwritten and prevent subclasses from inadvertently destroying immutability

2. Protective copy

However, some students will say that when using strings, there are also some methods related to modification, such as substring. Let's take substring as an example:

public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

It is found that it calls the String construction method to create a new String, and then enter this construction to see whether the final char[] value has been modified:

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

It turns out that neither. When constructing a new string object, a new char[] value will be generated to copy the content. This method of avoiding sharing by creating replica objects is called [protective copy]

3. final principle

public class TestFinal {
 final int a = 20; }

Bytecode

0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 20
7: putfield #2 // Field a:I
 <-- Write barrier
10: return

It is found that the assignment of the final variable will also be completed through the putfield instruction. Similarly, a write barrier will be added after this instruction to ensure that it will not be 0 when other threads read its value

More detailed explanation of final principle:( https://forlogen.blog.csdn.net/article/details/105655338)

In fact, like ordinary assignment, final can be implemented using putfield bytecode instructions, and a write barrier is added after the bytecode to ensure thread safety

The write barrier prevents reordering of instructions

The writing of a final field in any constructor and the subsequent assignment of the reference of this construction object to another reference variable cannot be reordered

Implication: the final field of the object has been properly initialized before the object reference is visible to any thread.

When reading a reference containing the final domain object for the first time, and then reading the final domain for the first time, the two operations cannot be reordered

Implication: the value of the final domain can only be read after the reference containing the final domain object is obtained.

Topics: JavaEE Back-end