Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the reason why StringBuilder is not thread safe?

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the relevant knowledge that StringBuilder is the cause of thread unsafety, the content is detailed and easy to understand, the operation is simple and fast, and it has certain reference value. I believe you will gain something after reading this StringBuilder is the cause of thread unsafety. Let's take a look at it.

Cause analysis

If you look at the source code of StringBuilder or StringBuffer, you will say that because StringBuilder does not use thread synchronization in append operations, and almost most StringBuffer methods use the synchronized keyword for method-level synchronization.

The above statement is certainly true, as can be seen by comparing some of the source code of StringBuilder and StringBuffer.

The source code of append method for StringBuilder:

@ Overridepublic StringBuilder append (String str) {super.append (str); return this;}

The source code of append method for StringBuffer:

@ Overridepublic synchronized StringBuffer append (String str) {toStringCache = null; super.append (str); return this;}

There is certainly nothing wrong with the above conclusion, but it does not explain what makes StringBuilder's thread unsafe? Why use synchronized to ensure thread safety? What will happen if you don't use it?

Let's explain them one by one.

Exception example

Let's first run a code example to see if the results are consistent with our expectations.

@ Testpublic void test () throws InterruptedException {StringBuilder sb = new StringBuilder (); for (int I = 0; I

< 10; i++) { new Thread(() ->

{for (int j = 0; j)

< 1000; j++) { sb.append("a"); } }).start(); } // 睡眠确保所有线程都执行完 Thread.sleep(1000); System.out.println(sb.length());} 上述业务逻辑比较简单,就是构建一个StringBuilder,然后创建10个线程,每个线程中拼接字符串"a"1000次,理论上当线程执行完成之后,打印的结果应该是10000才对。 但多次执行上面的代码打印的结果是10000的概率反而非常小,大多数情况都要少于10000。同时,还有一定的概率出现下面的异常信息" Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException at java.lang.System.arraycopy(Native Method) at java.lang.String.getChars(String.java:826) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449) at java.lang.StringBuilder.append(StringBuilder.java:136) at com.secbro2.strings.StringBuilderTest.lambda$test$0(StringBuilderTest.java:18) at java.lang.Thread.run(Thread.java:748)9007线程不安全的原因 StringBuilder中针对字符串的处理主要依赖两个成员变量char数组value和count。StringBuilder通过对value的不断扩容和count对应的增加来完成字符串的append操作。 // 存储的字符串(通常情况一部分为字符串内容,一部分为默认值)char[] value;// 数组已经使用数量int count; 上面的这两个属性均位于它的抽象父类AbstractStringBuilder中。 如果查看构造方法我们会发现,在创建StringBuilder时会设置数组value的初始化长度。 public StringBuilder(String str) { super(str.length() + 16); append(str);} 默认是传入字符串长度加16。这就是count存在的意义,因为数组中的一部分内容为默认值。 当调用append方法时会对count进行增加,增加值便是append的字符串的长度,具体实现也在抽象父类中。 public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this;} 我们所说的线程不安全的发生点便是在append方法中count的"+="操作。我们知道该操作是线程不安全的,那么便会发生两个线程同时读取到count值为5,执行加1操作之后,都变成6,而不是预期的7。这种情况一旦发生便不会出现预期的结果。 抛异常的原因 回头看异常的堆栈信息,回发现有这么一行内容: at java.lang.String.getChars(String.java:826) 对应的代码就是上面AbstractStringBuilder中append方法中的代码。对应方法的源代码如下: public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd >

Value.length) {throw new StringIndexOutOfBoundsException (srcEnd);} if (srcBegin > srcEnd) {throw new StringIndexOutOfBoundsException (srcEnd-srcBegin);} System.arraycopy (value, srcBegin, dst, dstBegin, srcEnd-srcBegin);}

In fact, the exception occurs at the bottom of the JVM during the last line of arraycopy. The core operation of arraycopy is to copy the incoming String object into value.

The reason for the exception is that it is clear that the subscript of value is only 6, but the program has to access and operate the location where the subscript is 7, which of course runs abnormal.

So, why is it beyond such a position? This has something to do with the fact that the count we mentioned above is underadded. Before executing the str.getChars method, you also need to verify whether the current value has been used up according to count, and if so, expand the capacity. The corresponding methods in append are as follows:

EnsureCapacityInternal (count + len)

The specific implementation of ensureCapacityInternal:

Private void ensureCapacityInternal (int minimumCapacity) {/ / overflow-conscious code if (minimumCapacity-value.length > 0) {value = Arrays.copyOf (value, newCapacity (minimumCapacity);}}

Count was supposed to be 7 and the length of value was 6, and it was supposed to trigger expansion. However, because concurrency causes count to be 6, assuming that len is 1, the minimumCapacity passed is 7, and no expansion operation will be carried out. This causes a non-existent location to be accessed when the str.getChars method is executed later for the copy operation, so an exception is thrown.

By the way, let's take a look at the newCapacity method in the expansion method:

Private int newCapacity (int minCapacity) {/ / overflow-conscious code int newCapacity = (value.length

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report