StringBuilder是线程不安全的,而StringBufffer是线程安全的为什么呢?
首先我们要明白的是StringBuilder和StringBuffer的内部实现跟String类一样,都是通过访问一个char数组存储字符串的,不同时的是String类里面的char数组都是final修饰的,是不可变的,而StringBuilder和StringBuffer的char数组是可变的。
StringBuilder和StringBuffer都继承了AbstractStringBuilder
一.StringBuilder
我先先看一段代码
public class StringBuilderDemo {
public static void main(String[] args) throws InterruptedException {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++){
stringBuilder.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(stringBuilder.length());
}
}
我们能看到这段代码创建了10个线程,每个线程循环1000次往StringBuilder对象里面append字符。正常情况下代码应该输出10000,但是实际运行会输出什么呢?
ArrayIndexOutOfBoundsException异常(异常不是必现)。
1.解答
我们先看一下StringBuilder的两个成员变量(这两个成员变量实际上是定义在AbstractStringBuilder里面的,StringBuilder和StringBuffer都继承了AbstractStringBuilder)
1.StringBuilder的append()方法:
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
这里面又去调用了父类AbstractStringBuiler的append()方法
我们先直接看int spaceNeeded=cout+appendedLength;这并不是一个原子操作,假设cout为5,appendedLength为1,两个线程同时到了这一行,同时拿到的cout是5,但是第一个线程执行完之后是spaceNeeded是6,第二个线程得到的结果也是6并不是我们所想的7.这就是为什么得到的结果比1000小了。
2.为什么会抛出ArrayIndexOutBoundsException异常。
我们可以看一下ensureCapacityInternal()方法是检查StringBuilder对象的原char数组的容量能不能盛下新的字符串,如果盛不下就调用对char数组进行扩容【原来的2倍+2】。
Arrys.copyof()方法
getChars()方法:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
//中间省略了一些检查
...
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
二:StringBuffer
加了synchronized