我已经阅读了一些关于String
和StringBuilder
在Java程序设计语言中的好处和缺点的文章。在其中一篇文章中,作者写道:StringBuilders are not Thread safe so in multiple threads use StringBuffer
。
现在我无法理解其含义。
您能否告诉我Stirng
,StirngBuilder
和StringBuffer
的区别,特别是在“线程安全”的背景下。 (用代码描述可能非常好,但如果不可能,任何提示都将受到赞赏)。
答案 0 :(得分:12)
如果多个线程正在修改StringBuilder
的同一个实例,则结果可能是意外的 - 即某些修改可能会丢失。这就是为什么你应该在这种情况下使用StringBuffer。但是,如果每个线程StringBuilder
实例只能由一个线程修改,则最好使用StringBuilder
,因为它会更有效(线程安全带来性能成本)。
答案 1 :(得分:6)
如果多个线程尝试更改StringBuilder对象值,则结果将很奇怪。见下面的例子,
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方法,那么结果将是奇怪的(不可预测的结果)。
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
答案 2 :(得分:3)
StringBuilder
的线程安全问题是StringBuilder
上的方法调用不同步。
考虑StringBuilder.append(char)
方法的实现:
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;
语句,count
为1
。每个人都会将其角色写入value[1]
的缓冲区,然后更新count
。显然,那里只能存储一个字符......所以另一个字符将会丢失。此外,count
的增量之一可能会丢失。
更糟糕的是,即使两个线程同时到达那里,value[count++] = c;
语句也会失败。原因是Java内存模型说除非有正确的同步("发生在"关系之前),否则不能保证第二个线程会看到第一个线程所做的内存更新。实际发生的事情取决于是否以及何时将第一个线程的更新写入主存储器。
现在让我们看一下StringBuffer.append(char)
:
public synchronized StringBuffer append(char c) {
super.append(c); // calls the "AbstractStringBuilder.append" method above.
return this;
}
我们在此处看到append
方法为synchronized
。这意味着两件事:
两个线程不能同时在同一个append
对象上执行超类StringBuffer
方法。因此,第一种情况不可能发生。
synchronize
表示不同线程对happens before
的连续调用之间存在StringBuffer.append
。这意味着后来的线程保证可以看到在前一个线程中所做的更新。
String
案例再次不同。如果我们检查代码,我们将看到没有明显的同步。但那没关系,因为String
对象实际上是不可变的;即String
API中的任何方法都不会导致String
对象状态中的外部可观察更改。另外:
final
实例变量和构造函数的特殊行为意味着所有线程都会看到任何String
的正确初始状态。
在String
幕后可变的地方,无论线程是否看到hashCode()
的最新更改,hash
方法都能正常工作变量
参考文献:
答案 3 :(得分:2)
因为StringBuilder不是同步的,而StringBuffer是同步的。当在多线程环境中使用StringBuilder时,多个线程可以同时访问StringBuilder对象,并且无法预测它产生的输出因此StringBuilder不是线程安全...
使用StringBuffer我们可以克服线程安全的问题,其中StringBuffer是一个线程安全,因为它是同步的,一次只有一个线程可以访问,因此它产生的输出可以预期和预测。
答案 4 :(得分:0)
方法内部的StringBuilder是安全的。
public void addProperty(String name, String value) {
private StringBuilder sb = new StringBuilder("1=2");
if (value != null && value.length() > 0) {
if (sb.length() > 0) {
sb.append(',');
}
sb.append(name).append('=').append(value);
}
}