0


再谈StringBuilder为什么线程不安全以及带来的问题

1 缘起

比较有意思的是,学习锁消除的过程中,有人讲到StringBuffer在方法内构建,不会被其他方法引用时,StringBuffer的锁会被消除,
于是,顺便看了一下同源的StringBuidler为什么线程不安全,以及为什么多线程不安全,和带来的问题,
有了这篇文章,分享出来,帮助读者轻松应对知识交流与考核。

2 StringBuilder

StringBuilder用于缓存字符串的容器,是StringBuffer的高性能版本,因为,StringBuilder适用于单线程,多线程下无法保证程序正常执行。建议优先使用StringBuilder,多数场景下,效率更高。
StringBuilder继承AbstractStringBuilder,而StringBuffer也是继承该类,所以,StringBuilder和StringBuffer是兼容的。

位置:java.lang.StringBuilder
在这里插入图片描述

2.1 StringBuilder线程不安全是指什么?

StringBuilder的多线程不安全是指程序不能正常执行,即数组越界异常,而不是数据错乱问题。

2.1.1 测试样例

packagecom.monkey.java_study.lock;/**
 * 锁消除测试.
 *
 * @author xindaqi
 * @since 2023-06-23 16:04
 */publicclassLockEliminateTest{staticStringBuilder sb2 =newStringBuilder();publicstaticvoidmain(String[] args)throwsException{LockEliminateTest lockEliminateTest =newLockEliminateTest();for(int i =0; i <100; i++){newThread(()->{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 测试结果

多线程情况下,StringBuilder出现数组越界,无法正常添加数据,程序异常。
异常信息如下图所示:
在这里插入图片描述

2.2 为什么线程不安全?

结论:因为AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
先从append方法说起,StringBuilder继承AbstractStringBuilder,而StringBuilder中直接使用了父类的append方法。
在这里插入图片描述
既然StringBuilder直接使用了AbstractStringBuilder的append方法,我们直接探究这个父类的方法,源码如下,使用的全局变量count作为数组扩容的参数:ensureCapacityInternal。

在这里插入图片描述
数组扩容系列操作源码如下,如果传入的值大于存储数据的字符数组长度,则拷贝数据到新的数组中,保证数据可以正常存储。
问题就出现在这里,多线程出现数组没有及时扩充,使用str.getChars时,导致数组越界。
现在推演一下:
缓存数据的数组arr长度为10,已占用6个空位,还剩4个空位
即初始count为6,
两个线程T1和T2,均携带4个字符,向数组写入数据,
此时:minimumCapacity=count+len=6+4=10
线程T1:
miniumCapacity-value.length=0不满足扩容条件,
此时数据写入arr,刚好填满,还未执行到count+=len时,
线程T2:
开始执行ensureCapacityInternal,此时,count+len=6+4=10,依旧不会触发扩容,
线程T1:
执行count+=len,即count=10,
T2执行到str.getChars时,count为10,出现数组不够用,导致数组越界。
即:T2执行str.getChars(0, 4, [a1, …, a10], 10)
srcBegin=0
srcEnd=4
dst=[a1, …, a10]
dstBegin=10
在826行出现异常,即Sysgem.arrayCopy
这个时候dst长度为10,而新写入的数据从10开始,因此,无法再写入数据了,抛出异常。

在这里插入图片描述

在这里插入图片描述
多线程出现的时间差,导致数组没有正常被扩容,
最后无法写入数据,数组越界后抛出异常。
测试样例:

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));

同款异常如下:
在这里插入图片描述

2.3 StringBuilder多线程不安全带来的问题?

程序无法正常执行(抛出数组越界的异常),而不是数据错乱问题。

3 小结

(1)StringBuilder的多线程不安全是指程序不能正常执行,即数组越界异常,而不是数据错乱问题。
(2)AbstractStringBuilder中的append方法中使用了全局变量count,多线程无法保证数组正常扩充,因此,出现数组越界的异常。
(3)StringBuilde多线程不安全带来的问题是程序无法正常运行。


本文转载自: https://blog.csdn.net/Xin_101/article/details/131358632
版权归原作者 天然玩家 所有, 如有侵权,请联系我们删除。

“再谈StringBuilder为什么线程不安全以及带来的问题”的评论:

还没有评论