StringBuilder的为什么线程不安全

引言

StringBuilder 和 StringBuffer 的区别在哪?
StringBuilder 是线程不安全的
StringBuffer 是线程安全的用了synchronized 关键字因此是线程安全的。

分析

StringBuffer 和StringBuilder 都是内部和String一样的 都是通过一个字符数组存储字符串的,不同的是字符串里的Char 数组是final 修饰,是不可变的,而StringBuffer和StringBuilder是可以变化的。

StringBuffer为什么线程不安全

AbstractStringBuilder里面的,StringBuilder的和的StringBuffer都继承了AbstractStringBuilder)

//存储字符串的具体内容
char[] value;
//已经使用的字符数组的数量
int count;

再看的StringBuilder的追加()方法:

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

StringBuilder的追加的()方法调用的父类AbstractStringBuilder的追加()方法

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

对于count + =len;不是一个原子操作 两个线程同时执行假设都是 计数器为 10 执行完后 就会变成11 而不是12

2,为什么会抛出ArrayIndexOutOfBoundsException异常异常。

我们看回AbstractStringBuilder的追加()方法源码的第五行,ensureCapacityInternal()方法是检查StringBuilder的对象的原字符数组的容量能不能盛下新的字符串,如果盛不下就调用expandCapacity()方法对字符数组进行扩容。

private void ensureCapacityInternal(int minimumCapacity) {
       // overflow-conscious code
   if (minimumCapacity - value.length > 0)
       expandCapacity(minimumCapacity);
}

扩容的逻辑就是新一个新的字符数组,新的字符数组的容量是原来字符数组的两倍再加2,再通过System.arryCopy()函数将原数组的内容复制到新数组,最后将指针指向新的字符数组。

void expandCapacity(int minimumCapacity) {
   //计算新的容量
   int newCapacity = value.length * 2 + 2;
   //中间省略了一些检查逻辑
   ...
   value = Arrays.copyOf(value, newCapacity);
}

Arrys.copyOf()方法

public static char[] copyOf(char[] original, int newLength) {
   char[] copy = new char[newLength];
   //拷贝数组
   System.arraycopy(original, 0, copy, 0,
                        Math.min(original.length, newLength));
   return copy;
}

则GetChars()方法

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
   //中间省略了一些检查
   ...   
   System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
   }

拷贝流程见下图
在这里插入图片描述
假设现在有两个线程同时执行了的StringBuilder的追加()方法,两个线程都执行完了第五行的ensureCapacityInternal()方法,此刻计数= 5。

在这里插入图片描述
这个时候线程1个的CPU时间片用完了,线程2继续执行。线程2执行完整个追加()方法后计数变成6了
在这里插入图片描述
线程1继续执行第六行的str.getChars()方法的时候拿到的计数值就是6了,执行字符数组拷贝的时候就会抛出ArrayIndexOutOfBoundsException异常异常。
至此,StringBuilder的为什么不安全已经分析完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值