为什么线程不安全
StringBuffer和StringBuilder都继承了AbstractStringBuilder,而该抽象父类维护了三个值,其中count代表了容器中字符串的长度。
�我们再看一下常用的append方法
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
// 这一行的长度累加,不是原子性的,导致线程不安全
count += len;
return this;
}
我们再看一下继承子类都是怎样重写的
StringBuffer ,方法用synchronized加了同步锁。而且方法内所有方法都加了synchronized修饰,所以是线程安全的。
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuilder,方法没有任何变化,直接使用父类的方法。父类线程不安全,所以这里也是线程不安全。
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
怎样选择
String:不可变字符串;
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
总结:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。