答案:StringBuffer

为什么线程不安全

image.png
StringBuffer和StringBuilder都继承了AbstractStringBuilder,而该抽象父类维护了三个值,其中count代表了容器中字符串的长度。
image.png
�我们再看一下常用的append方法

  1. public AbstractStringBuilder append(String str) {
  2. if (str == null) {
  3. return appendNull();
  4. }
  5. int len = str.length();
  6. ensureCapacityInternal(count + len);
  7. putStringAt(count, str);
  8. // 这一行的长度累加,不是原子性的,导致线程不安全
  9. count += len;
  10. return this;
  11. }

我们再看一下继承子类都是怎样重写的
StringBuffer ,方法用synchronized加了同步锁。而且方法内所有方法都加了synchronized修饰,所以是线程安全的。

  1. @Override
  2. @HotSpotIntrinsicCandidate
  3. public synchronized StringBuffer append(String str) {
  4. toStringCache = null;
  5. super.append(str);
  6. return this;
  7. }

StringBuilder,方法没有任何变化,直接使用父类的方法。父类线程不安全,所以这里也是线程不安全。

  1. @Override
  2. @HotSpotIntrinsicCandidate
  3. public StringBuilder append(String str) {
  4. super.append(str);
  5. return this;
  6. }

怎样选择

String:不可变字符串;
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
总结:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。