首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >“StringBuilders不是线程安全”是什么意思?

“StringBuilders不是线程安全”是什么意思?
EN

Stack Overflow用户
提问于 2014-11-11 07:59:33
回答 5查看 15.8K关注 0票数 16

我读过一些关于StringStringBuilder在Java编程语言中的优缺点的文章。在其中一篇文章中,提交人提到:

StringBuilder不是线程安全的,所以在多线程中使用StringBuffer.

不幸的是,我无法理解这意味着什么。请您解释一下StringStringBuilderStringBuffer之间的区别,特别是在“线程安全”的上下文中。

如果您能用代码示例进行描述,我将不胜感激。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-11-11 08:01:29

如果多个线程正在修改同一个StringBuilder实例,结果可能是意外的--即一些修改可能会丢失。这就是为什么在这种情况下应该使用StringBuffer的原因。但是,如果每个线程StringBuilder实例只能由一个线程修改,那么最好使用StringBuilder,因为它会更高效(线程安全性带来性能成本)。

票数 17
EN

Stack Overflow用户

发布于 2014-11-11 08:07:52

如果多个线程试图更改StringBuilder对象值,则结果将是奇怪的。参见下面的示例,

代码语言:javascript
复制
private StringBuilder sb = new StringBuilder("1=2");

public void addProperty(String name, String value) {
    if (value != null && value.length() > 0) {
        if (sb.length() > 0) {
            sb.append(',');
        }
        sb.append(name).append('=').append(value);
    }
}

如果许多线程调用addProperty方法,那么结果将是奇怪的(不可预测的结果)。

代码语言:javascript
复制
Thread1: addProperty("a", "b");
Thread2: addProperty("c", "d");
Thread3: addProperty("e", "f");

最后,当您调用sb.toString()时,结果将是不可预测的。例如,它可能带来类似于1=2,ac=d=b,e=f的输出,但您的期望是1=2,a=b,c=d,e=f

票数 9
EN

Stack Overflow用户

发布于 2014-11-11 10:36:41

StringBuilder的线程安全问题是对StringBuilder的方法调用不同步。

考虑一下StringBuilder.append(char)方法的实现:

代码语言:javascript
复制
public StringBuilder append(boolean b) {
    super.append(b);
    return this;
}

// from the superclass
public AbstractStringBuilder append(char c) {
     int newCount = count + 1;
     if (newCount > value.length)
         expandCapacity(newCount);
     value[count++] = c;
     return this;
 }

现在假设您有两个线程共享一个StringBuilder实例,并且都试图同时追加一个字符。假设它们同时到达value[count++] = c;语句,而count1。每个字符都将写入value[1]的缓冲区中,然后更新count。很明显只有一个字符可以存储在那里..。所以另一只就会迷路。此外,count的增量之一可能会丢失。

更糟糕的是,即使两个线程没有同时到达那里,value[count++] = c;语句也可能失败。原因是Java内存模型说,除非有适当的同步(或者更准确地说,在关系之前发生),否则不能保证第二个线程会看到第一个线程所做的内存更新。实际情况取决于是否和何时将第一个线程的更新写入主内存。

现在让我们来看看StringBuffer.append(char)

代码语言:javascript
复制
public synchronized StringBuffer append(char c) {
    super.append(c);  // calls the "AbstractStringBuilder.append" method above.
    return this;
}

这里我们看到append方法是synchronized。这意味着两件事:

  • 两个线程不能同时在同一个append对象上执行超类StringBuffer方法。因此,第一种情况不可能发生。

  • synchronize意味着在不同线程对StringBuffer.append进行的连续调用之间发生了一个事件。这意味着保证后面的线程能够看到在前面的线程中所做的更新。

String的情况又不同了。如果我们检查代码,我们将看到没有公开的同步。但这是可以的,因为String对象实际上是不可变的;也就是说,在String API中没有方法会导致String对象的状态发生外部可观察的变化。此外:

  • final实例变量和构造函数的特殊行为意味着所有线程都将看到任何String的正确初始状态。

  • 在幕后String是可变的地方,无论线程是否看到对hash变量的最新更改,hashCode()方法都将正确工作。

参考文献:

  • StringBuilder - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/lang/StringBuilder.java的源代码
  • StringBuffer - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/lang/StringBuffer.java的源代码
  • 字符串的源代码- http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/lang/String.java
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26860173

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档