引言
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的为什么不安全已经分析完了。