Let’s talk about why StringBuilder is not thread-safe and the problems it brings

1 Dependence

What is more interesting is that in the process of learning lock elimination, someone said that when StringBuffer is built in a method and will not be referenced by other methods, the StringBuffer lock will be eliminated. So, by the way, I took a look at why the thread of the same-source StringBuidler is not safe
. , and why multi-threading is not safe, and the problems it brings,
with this article, share it to help readers easily cope with knowledge exchange and assessment.

2 StringBuilder

StringBuilder is a container for caching strings, which is a high-performance version of StringBuffer, because StringBuilder is suitable for single-thread, and the normal execution of the program cannot be guaranteed under multi-thread. It is recommended to use StringBuilder first, which is more efficient in most scenarios.
StringBuilder inherits from AbstractStringBuilder, and StringBuffer also inherits from this class, so StringBuilder and StringBuffer are compatible.

Location: java.lang.StringBuilder
insert image description here

2.1 What does StringBuilder thread unsafe mean?

The multi-thread insecurity of StringBuilder means that the program cannot be executed normally , that is, the array out-of-bounds exception , rather than the problem of data confusion.

2.1.1 Test sample

package com.monkey.java_study.lock;

/**
 * 锁消除测试.
 *
 * @author xindaqi
 * @since 2023-06-23 16:04
 */
public class LockEliminateTest {
    
    

    static StringBuilder sb2 = new StringBuilder();

    public static void main(String[] args) throws Exception {
    
    
        LockEliminateTest lockEliminateTest = new LockEliminateTest();
        for (int i = 0; i < 100; i++) {
    
    
            new Thread(() -> {
    
    
                for (int j = 0; j < 1000; j++) {
    
    
                    sb2.append("test");
                }
            }).start();
        }
        Thread.sleep(100);
        System.out.format(">>>>>>StringBuilder:length:%d\n", sb2.length());
    }
}

2.1.2 Test results

In the case of multi-threading, the StringBuilder has an array out of bounds, cannot add data normally, and the program is abnormal.
The exception information is shown in the figure below:
insert image description here

2.2 Why is thread unsafe?

Conclusion: Because the global variable count is used in the append method in AbstractStringBuilder, multi-threading cannot guarantee the normal expansion of the array, so an exception occurs that the array is out of bounds.
Let's start with the append method. StringBuilder inherits AbstractStringBuilder, and StringBuilder directly uses the append method of the parent class.
insert image description here
Since StringBuilder directly uses the append method of AbstractStringBuilder, we directly explore the method of this parent class. The source code is as follows, and the global variable count is used as a parameter for array expansion: ensureCapacityInternal.

insert image description here
The source code of the array expansion series operation is as follows. If the value passed in is greater than the length of the character array storing the data, copy the data to a new array to ensure that the data can be stored normally.
The problem arises here, the multi-threaded array is not expanded in time, and when str.getChars is used, the array is out of bounds.
Now deduce it:
the length of the array arr of the cached data is 10, 6 vacancies have been occupied, and 4 vacancies are left,
that is, the initial count is 6, and
the two threads T1 and T2 both carry 4 characters and write data to the array
. When: minimumCapacity=count+len=6+4=10
Thread T1:
miniumCapacity-value.length=0 does not meet the expansion conditions,
at this time the data is written into arr, just filled, and before count+=len is executed,
thread T2 :
Start to execute ensureCapacityInternal, at this time, count+len=6+4=10, still will not trigger expansion,
thread T1:
execute count+=len, that is, count=10,
when T2 executes str.getChars , the count is 10, The array is not enough, resulting in the array out of bounds.
That is: T2 executes str.getChars(0, 4, [a1, …, a10], 10)
srcBegin=0
srcEnd=4
dst=[a1, …, a10]
dstBegin=10

An exception occurs on line 826, that is, the length of dst in Sysgem.arrayCopy is 10 at this time, and the newly written data starts from 10, so the data can no longer be written, and an exception is thrown.

insert image description here

insert image description here
The time difference caused by multi-threading caused the array not to be expanded normally, and
finally the data could not be written, and an exception was thrown after the array crossed the bounds.
Test sample:

char[] dst = {
    
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
        String str = "test";
        str.getChars(0, 4, dst, 10);
        System.out.println(">>>>>Char array:" + Arrays.toString(dst));

The exception of the same paragraph is as follows:
insert image description here

2.3 Problems caused by StringBuilder's multi-thread insecurity?

The program cannot be executed normally (throws an exception that the array is out of bounds), not the problem of data confusion.

3 Summary

(1) The multi-thread insecurity of StringBuilder means that the program cannot be executed normally , that is, an array out-of-bounds exception , rather than a data disorder problem.
(2) The append method in AbstractStringBuilder uses the global variable count, and multi-threading cannot guarantee the normal expansion of the array. Therefore, an exception occurs that the array is out of bounds.
(3) The problem caused by StringBuilde's multi-thread insecurity is that the program cannot run normally.

Guess you like

Origin blog.csdn.net/Xin_101/article/details/131358632