我知道StringBuffer和StringBuilder之间的区别。 read here!
通常,正如javadoc所说,
在可能的情况下,建议首先使用此类优先于StringBuffer,因为在大多数实现中它会更快。
但是,StringBuilder的javadoc也说:
StringBuilder
的实例不适合多线程使用。如果需要这样的同步,则建议使用{@link java.lang.StringBuffer}
所以,我想知道,StringBuffer首选的情况真的存在吗?由于可变字符串主要用于单个线程,任何人都可以给我一个并行的实际场景,首选StringBuffer吗?
答案 0 :(得分:7)
StringBuffer是线程安全的原因是,在设计第一版java api的那天,人们以不同于现在的方式接近并发。流行的观点是对象应该是线程安全的 - 因为Java支持线程,人们可能在多个线程中使用任何JDK类。后来,当Java开始针对执行时间进行优化时,那些不必要的同步块的成本开始成为一个问题,因此新的API被设计为不同步。再过一段时间,JVM开始优化锁定,以至于无争议的锁定基本上是免费的,这使整个决策变得没有实际意义。
StringBuffer仍然是线程安全的,因为旧代码可能依赖于它是线程安全的。这远非典型用途,但可以想象。
例如,假设您正在编写将日志条目转发到中央服务器的日志文件追加器。由于我们不想在等待网络I / O时阻塞调用者,因此我们在专用线程中执行此操作。其他线程会在StringBuffer中累积其日志条目:
class RemoteLogger implements Runnable, Appender {
final StringBuffer buffer = new StringBuffer();
void append(String s) {
buffer.append(s);
}
public void run() {
for (;;) {
Thread.sleep(100);
String message = buffer.toString();
sendToServer(message);
buffer.delete(0, message.length());
}
}
}
答案 1 :(得分:4)
简单的答案是否定的。恕我直言,你不会使用StringBuffer而不是StringBuilder或其他类。使StringBuffer线程安全可以使你的代码更少线程安全,因为人们错误地认为如果你已经使用了StringBuffer,那么在不是这种情况下代码是线程安全的。
如果您已经使用过StringBuffer,那么在某些时候您必须使用synchronized,尽管大多数开发人员并不总是清楚,并且我已经看到很多错误(即使在成熟的库中)没有完成或者没有做得对。使用StringBuilder并在内部进行一致的锁定会好得多。
Why a synchronized StringBuffer was never a good idea.
是首选StringBuffer确实存在的情况
有一个用例;你有一个只接受API中的StringBuffer的库。由于上面提到的原因,这是糟糕的设计,但库不是完美的。 ;)
答案 2 :(得分:3)
确实可以同时访问文本缓冲区的任何地方。
作为一个例子,如何让多个编写器线程通过网络输出数据。在这种情况下,它们可能共享一个公共文本缓冲区并直接写入它,当缓冲区已满时,它可以通过网络发送。
答案 3 :(得分:2)
以下程序有时会在使用StringBuilder时抛出异常,但在使用StringBuffer时永远不会抛出异常。
程序:
public class StringBuilderConcurrent {
static final StringBuilder sb = new StringBuilder(); // shared memory
public static void main(String[] args) throws Exception {
int NUM_WRITERS = 300;
ArrayList<WriterThread> threads = new ArrayList<WriterThread>(NUM_WRITERS);
for (int i = 0; i < NUM_WRITERS; i++) {
WriterThread wt = new WriterThread("writerThread" + i);
threads.add(wt);
wt.start();
}
for (int i = 0; i < threads.size(); i++) {
threads.get(i).join();
}
System.out.println(sb);
}
public static class WriterThread extends Thread {
public WriterThread(String name) {
super(name);
}
public void run() {
String nameNl = this.getName() + "\n";
for (int i = 1; i < 20; i++) {
sb.append(nameNl);
}
}
};
}
因为StringBuilder(sb
)不是线程安全的,所以让多个线程将数据写入sb
可能会导致sb
被破坏(例如,意外的空字符,一些单词的字母穿插一些其他字母的字母)。 sb
的内部状态也可能变得不一致,以至于可能抛出异常:
Exception in thread "writerThread0" java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:854)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:391)
at java.lang.StringBuilder.append(StringBuilder.java:119)
at test.StringBuilderConcurrent$WriterThread.run(StringBuilderConcurrent.java:35)
以下程序与第一个程序完全相同,只是它使用StringBuffer而不是StringBuilder。它永远不会遇到ArrayIndexOutOfBoundsException。
public class StringBufferConcurrent {
static final StringBuffer sb = new StringBuffer(); // shared memory
public static void main(String[] args) throws Exception {
int NUM_WRITERS = 300;
ArrayList<WriterThread> threads = new ArrayList<WriterThread>(NUM_WRITERS);
for (int i = 0; i < NUM_WRITERS; i++) {
WriterThread wt = new WriterThread("writerThread" + i);
threads.add(wt);
wt.start();
}
for (int i = 0; i < threads.size(); i++) {
threads.get(i).join();
}
System.out.println(sb);
}
public static class WriterThread extends Thread {
public WriterThread(String name) {
super(name);
}
public void run() {
String nameNl = this.getName() + "\n";
for (int i = 1; i < 20; i++) {
sb.append(nameNl);
}
}
};
}
这些程序是否代表“现实世界”问题是一个相当主观的问题。我会把这个判断留给听众。