为啥使用 StringBuilder? StringBuffer 可以与多个线程以及一个线程一起使用吗?

Posted

技术标签:

【中文标题】为啥使用 StringBuilder? StringBuffer 可以与多个线程以及一个线程一起使用吗?【英文标题】:Why use StringBuilder? StringBuffer can work with multiple thread as well as one thread?为什么使用 StringBuilder? StringBuffer 可以与多个线程以及一个线程一起使用吗? 【发布时间】:2011-09-04 16:18:49 【问题描述】:

假设我们的应用程序只有一个线程。而我们使用的是StringBuffer,那么有什么问题呢?

我的意思是如果StringBuffer 可以通过同步处理多个线程,那么使用单线程有什么问题?

为什么要改用StringBuilder

【问题讨论】:

【参考方案1】:

StringBuilder 应该快一点(小),因为它不是同步的(线程安全的)。

您会注意到在真正繁重的应用程序中的差异。

通常应优先使用 StringBuilder 类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。

http://download.oracle.com/javase/6/docs/api/java/lang/StringBuffer.html

【讨论】:

@MANISH PATHAK - 请阅读答案 - 问题:Why use StringBuilder instead? - 答案:StringBuilder is supposed to be a (tiny) bit faster(这篇文章的第一句话) 澄清:即使一个线程正在使用一个对象,同步方法也会强制线程获取和释放每次访问的监视器。这会减慢程序的速度。【参考方案2】:

StringBuilder 具有更好的性能,因为它的方法是不同步的。

因此,如果您不需要同时构建字符串(无论如何这是一个相当不典型的场景),则无需“支付”不必要的同步开销。

【讨论】:

你能描述一下那个场景吗? @Manish - 不,我不能,因为实际上我无法想象我必须选择 StringBuffer 而不是 StringBuilder 的场景。 谢谢安德烈亚斯...我从上面的帖子中得到它。谢谢大家【参考方案3】:

StringBuffers 是线程安全的,这意味着它们具有控制访问的同步方法,因此一次只有一个线程可以访问 StringBuffer 对象的同步代码。因此,StringBuffer 对象通常可以安全地用于多线程环境中,其中多个线程可能试图同时访问同一个 StringBuffer 对象。

StringBuilder's 访问不同步,因此它不是线程安全的。通过不同步,StringBuilder 的性能可以优于 StringBuffer。因此,如果您在单线程环境中工作,使用 StringBuilder 而不是 StringBuffer 可能会提高性能。这也适用于其他情况,例如只有一个线程将访问 StringBuilder 对象的 StringBuilder 局部变量(即方法中的变量)。

所以,更喜欢StringBuilder,因为,

性能提升小。 StringBuilder 是 StringBuffer 类的 1:1 替代品。 StringBuilder 不是线程同步的,因此在大多数 Java 实现中表现更好

看看这个:

Don't Use StringBuffer! StringBuffer vs. StringBuilder performance comparison

【讨论】:

最好说 StringBuffer 是“线程安全的”,但很容易编造两个线程争用同一缓冲区导致竞争条件的场景。即使您需要在线程之间共享一个字符串缓冲区,您也很可能需要对它实施比它显示的同步方法更高级别的控制。因此,我想不出很多使用 StringBuffer 的理由,除非第 3 方库要求您使用它,或者用于遗留/利基用途。 StringBuilder 并不是 StringBuffer 的 1:1 替代品。例如,MatcherappendReplacement 方法需要 StringBuffer 作为其第一个参数。【参考方案4】:

StringBuffer 在单线程应用程序中没有错误。它和StringBuilder 一样好用。

唯一的区别是所有同步方法所增加的微小开销,这在单线程应用程序中没有任何优势。

我认为引入StringBuilder主要 原因是编译器在编译包含String 连接的代码时使用StringBuffer(现在是StringBuilder):在那些案例同步从不是必要的,将所有这些地方替换为未同步的StringBuilder 可以提供小的性能改进。

【讨论】:

【参考方案5】:

在多个线程中使用 StringBuffer 几乎是无用的,实际上几乎不会发生。

考虑以下

Thread1: sb.append(key1).append("=").append(value1);
Thread2: sb.append(key2).append("=").append(value2);

每个追加都是同步的,但线程可以在任何时候停止,因此您可以拥有以下任何组合以及更多

key1=value1key2=value2
key1key2==value2value1
key2key1=value1=value2
key2=key1=value2value1

这可以通过一次同步整行来避免,但这违背了使用 StringBuffer 而不是 StringBuilder 的意义。

即使你有一个正确同步的视图,它也比创建整行的线程本地副本更复杂,例如StringBuilder 并一次将行记录到 Writer 之类的类中。

【讨论】:

【参考方案6】:

这将帮助你们, Be Straight Builder 比 Buffer 快,

public class ConcatPerf 
        private static final int ITERATIONS = 100000;
        private static final int BUFFSIZE = 16;

        private void concatStrAdd() 
            System.out.print("concatStrAdd   -> ");
            long startTime = System.currentTimeMillis();
            String concat = new String("");
            for (int i = 0; i < ITERATIONS; i++) 
                concat += i % 10;
            
            //System.out.println("Content: " + concat);
            long endTime = System.currentTimeMillis();
            System.out.print("length: " + concat.length());
            System.out.println(" time: " + (endTime - startTime));
        

        private void concatStrBuff() 
            System.out.print("concatStrBuff  -> ");
            long startTime = System.currentTimeMillis();
            StringBuffer concat = new StringBuffer(BUFFSIZE);
            for (int i = 0; i < ITERATIONS; i++) 
                concat.append(i % 10);
            
            long endTime = System.currentTimeMillis();
            //System.out.println("Content: " + concat);
            System.out.print("length: " + concat.length());
            System.out.println(" time: " + (endTime - startTime));
        

        private void concatStrBuild() 
            System.out.print("concatStrBuild -> ");
            long startTime = System.currentTimeMillis();
            StringBuilder concat = new StringBuilder(BUFFSIZE);
            for (int i = 0; i < ITERATIONS; i++) 
                concat.append(i % 10);
            
            long endTime = System.currentTimeMillis();
           // System.out.println("Content: " + concat);
            System.out.print("length: " + concat.length());
            System.out.println(" time: " + (endTime - startTime));
        

        public static void main(String[] args) 
            ConcatPerf st = new ConcatPerf();
            System.out.println("Iterations: " + ITERATIONS);
            System.out.println("Buffer    : " + BUFFSIZE);

            st.concatStrBuff();
            st.concatStrBuild();
            st.concatStrAdd();
        
    

Output  

    run:
    Iterations: 100000
    Buffer    : 16
    concatStrBuff  -> length: 100000 time: 11
    concatStrBuild -> length: 100000 time: 4
    concatStrAdd   -> 

【讨论】:

【参考方案7】:

Manish,虽然只有一个线程在您的 StringBuffer 实例上运行,但在调用 StringBuffer 实例的任何方法时,在获取和释放该实例上的监视器锁时都会产生一些开销。因此 StringBuilder 是单线程环境下的首选。

【讨论】:

【参考方案8】:

同步对象的成本很高。不要将程序视为独立的实体;当您阅读这些概念并将它们应用于您在问题详细信息中提到的小程序时,这不是问题,当我们想要扩展系统时会出现问题。在这种情况下,您的单线程程序可能依赖于其他几个方法/程序/实体,因此同步对象可能会在性能方面导致严重的编程复杂性。因此,如果您确定不需要同步对象,那么您应该使用 StringBuilder,因为它是一种很好的编程习惯。最后我们想学习编程来制作可扩展的高性能系统,这就是我们应该做的!

【讨论】:

以上是关于为啥使用 StringBuilder? StringBuffer 可以与多个线程以及一个线程一起使用吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java StringBuffer 和 StringBuilder 类

Java StringBuffer和StringBuilder类

StringBuilder

StringBuilder

有字符串时为啥要使用 StringBuilder?

StringBuffer和StringBuilder类