StringBuilder 和 StringBuffer 之间的区别

StringBufferStringBuilder之间的主要区别是什么?在决定其中任何一个时是否存在性能问题?

答案

StringBuffer已同步,而StringBuilder未同步。

StringBuilderStringBuffer快,因为它不synchronized

这是一个简单的基准测试:

public class Main {
    public static void main(String[] args) {
        int N = 77777777;
        long t;

        {
            StringBuffer sb = new StringBuffer();
            t = System.currentTimeMillis();
            for (int i = N; i --> 0 ;) {
                sb.append("");
            }
            System.out.println(System.currentTimeMillis() - t);
        }

        {
            StringBuilder sb = new StringBuilder();
            t = System.currentTimeMillis();
            for (int i = N; i > 0 ; i--) {
                sb.append("");
            }
            System.out.println(System.currentTimeMillis() - t);
        }
    }
}

一个试运行给出的数字2241 msStringBuffer VS 753 msStringBuilder

基本上, StringBuffer方法是同步的,而StringBuilder不同步。

这些操作 “几乎” 相同,但是在单个线程中使用同步方法是过大的。

差不多了。

引用StringBuilder API

此类 [StringBuilder] 提供与 StringBuffer 兼容的 API, 但不保证同步 。此类设计为在单线程正在使用字符串缓冲区的地方(通常是这种情况)来代替 StringBuffer。在可能的情况下,建议优先使用此类而不是 StringBuffer,因为在大多数实现中它会更快。

因此它被替代了。

VectorArrayList也发生了同样的情况。

但是需要借助示例来获得明显的不同吗?

StringBuffer 或 StringBuilder

除非您确实试图在线程之间共享缓冲区,否则只需使用StringBuilderStringBuilder是原始同步StringBuffer类的未同步(开销较小 = 更有效)的弟弟。

首先是StringBuffer 。 Sun 在所有情况下都关心正确性,因此他们将其同步以使其具有线程安全性,以防万一。

StringBuilder稍后发布。 StringBuffer大多数用法是单线程的,不必要地支付了同步费用。

由于StringBuilder是一个简易替换StringBuffer不同步,就不会有任何的例子之间的差异。

如果您线程之间试图分享,你可以使用StringBuffer ,但考虑到更高级别的同步是否是必要的,例如,也许不是使用 StringBuffer 的问题,您应该同步的是使用 StringBuilder 方法。

首先让我们看一下相似之处 :StringBuilder 和 StringBuffer 都是可变的。这意味着您可以在同一位置更改它们的内容。

区别 :StringBuffer 是可变的,并且也是同步的。其中,StringBuilder 是可变的,但默认情况下不同步。

同步(synchronization)的含义 :当某事物同步时,多个线程可以访问并对其进行修改,而不会出现任何问题或副作用。 StringBuffer 是同步的,因此您可以将其与多个线程一起使用而不会出现任何问题。

何时使用哪一个? StringBuilder:当您需要一个可以修改的字符串时,只有一个线程正在访问和修改它。 StringBuffer:当您需要一个可以修改的字符串,并且有多个线程正在访问和修改它时。

注意 :不要不必要地使用 StringBuffer,即,如果只有一个线程正在修改和访问它,则不要使用它,因为它具有大量用于同步的锁定和解锁代码,这将不必要地占用 CPU 时间。除非需要,否则不要使用锁。

在单线程中,由于 JVM 优化, StringBuffer 不会比 StringBuilder 显着慢 。在多线程中,您不能安全地使用 StringBuilder。

这是我的测试(不是基准测试,只是测试):

public static void main(String[] args) {

    String withString ="";
    long t0 = System.currentTimeMillis();
    for (int i = 0 ; i < 100000; i++){
        withString+="some string";
    }
    System.out.println("strings:" + (System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuffer buf = new StringBuffer();
    for (int i = 0 ; i < 100000; i++){
        buf.append("some string");
    }
    System.out.println("Buffers : "+(System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuilder building = new StringBuilder();
    for (int i = 0 ; i < 100000; i++){
        building.append("some string");
    }
    System.out.println("Builder : "+(System.currentTimeMillis() - t0));
}

结果:
字符串:319740
缓冲液: 23
建造者:7!

因此,Builder 比 Buffers 快,而 WAY 比字符串连接快。现在,我们将Executor用于多个线程:

public class StringsPerf {

    public static void main(String[] args) {

        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        //With Buffer
        StringBuffer buffer = new StringBuffer();
        for (int i = 0 ; i < 10; i++){
            executorService.execute(new AppendableRunnable(buffer));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Buffer : "+ AppendableRunnable.time);

        //With Builder
        AppendableRunnable.time = 0;
        executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        StringBuilder builder = new StringBuilder();
        for (int i = 0 ; i < 10; i++){
            executorService.execute(new AppendableRunnable(builder));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Builder: "+ AppendableRunnable.time);

    }

   static void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown(); // code reduced from Official Javadoc for Executors
        try {
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (Exception e) {}
    }
}

class AppendableRunnable<T extends Appendable> implements Runnable {

    static long time = 0;
    T appendable;
    public AppendableRunnable(T appendable){
        this.appendable = appendable;
    }

    @Override
    public void run(){
        long t0 = System.currentTimeMillis();
        for (int j = 0 ; j < 10000 ; j++){
            try {
                appendable.append("some string");
            } catch (IOException e) {}
        }
        time+=(System.currentTimeMillis() - t0);
    }
}

现在,StringBuffers 需要157 毫秒才能完成 100000 次追加。这不是相同的测试,但是与之前的 37 ms 相比,您可以放心地假设StringBuffers 追加在使用 multithreading 时较慢 。其原因是,在 JIT / 热点 / 编译器 / 东西使得优化,当它检测到有没有必要检查锁。

但是, 使用 StringBuilder 可以使用 java.lang.ArrayIndexOutOfBoundsException ,因为并发线程会尝试在不应添加的位置添加内容。

结论是您不必追求 StringBuffers。在有线程的地方,请先考虑一下它们在做什么,然后再尝试增加几纳秒的时间。

StringBuilder 是 Java 1.5 中引入的,因此它不适用于早期的 JVM。

Javadocs

StringBuilder 类提供与 StringBuffer 兼容的 API,但不保证同步。此类设计为在单线程正在使用字符串缓冲区的地方(通常是这种情况)来代替 StringBuffer。在可能的情况下,建议优先使用此类而不是 StringBuffer,因为在大多数实现中它会更快。

很好的问题

我注意到这是差异:

StringBuffer:-

StringBuffer is  synchronized
StringBuffer is  thread-safe
StringBuffer is  slow (try to write a sample program and execute it, it will take more time than StringBuilder)

StringBuilder:-

StringBuilder is not synchronized 
 StringBuilder is not thread-safe
 StringBuilder performance is better than StringBuffer.

普通事:-

两者的签名方法相同。两者都是可变的。

StringBuilder 不是线程安全的。字符串缓冲区是。更多信息在这里

编辑:至于性能, 热点启动后,StringBuilder 是赢家。但是,对于较小的迭代,性能差异可以忽略不计。

StringBuffer

  • 同步,因此线程安全
  • 线程安全因此很慢

StringBuilder

  • 在 Java 5.0 中引入
  • 异步,因此快速高效
  • 用户明确需要进行同步(如果需要)
  • 您可以使用StringBuffer替换它,而无需进行其他任何更改