1.简介
字符串相关类型主要有这三种:String、StringBuffer、StringBuilder,其中 StringBuffer、StringBuilder 都是可以变的字符串类型,StringBuffer 在字符串拼接时使用 synchronized 来保障线程安全,如图:
因此在多线程字符串拼接中推荐使用 StringBuffer。StringBuilder 的使用方法和 StringBuffer 一样,它们都继承于 AbstractStringBuilder。
性能:String 每次修改相当于生成一个新对象,因此性能最低;StringBuffer 使用 synchronized 来保证线程安全,性能优于 String,但不如 StringBuilder;
2.StringBuilder线程安全问题
StringBuilder为什么线程不安全?
对比一下StringBuilder与StringBuffer的区别:
多线程情况下StringBuilder线程安全问题
StringBuilder stringBuilder = new StringBuilder();
for(int i =0; i<10; i++){
new Thread( () -> {
for(int j = 0; j<1000; j++){
stringBuilder.append("a");
}
}).start();
}
Thread.sleep(100);
System.out.println(stringBuilder.length());
每次运行,stringBuilder.length() 数值几乎都小于预期结果:10000,期间还会出现数组越界的异常。
原因分析:
进入SringBuilder append 方法,发现调用super.append(), 进入此方法,
扩容的逻辑就是:new一个新的char数组,新的char数组的容量是原来char数组的两倍再加2,再通过System.arryCopy()函数将原数组的内容复制到新数组,最后将指针指向新的char数组。
拷贝流程:
假设现在有两个线程同时执行了StringBuilder的append()方法,两个线程都执行完了第五行的ensureCapacityInternal()方法,此刻count=5.
这个时候线程1的cpu时间片用完了,线程2继续执行。线程2执行完整个append()方法后count变成6了。线程1继续执行第六行的str.getChars()方法的时候拿到的count值就是6了,执行char数组拷贝的时候就会抛出ArrayIndexOutOfBoundsException异常。