当StringBuilder更快时,为什么Java使用StringBuffer(synchronized)?

时间:2012-08-10 21:40:15

标签: java field stringbuilder stringbuffer

我正在查看Java反射类并注意到这段代码。让我想知道,当StringBuilder更快时,为什么Java使用StringBuffer?

Java不想使用最快的实现,还是有其他原因?

代码在Field.class中:

static String getTypeName(Class<?> type) {
    if (type.isArray()) {
        try {
            Class<?> cl = type;
            int dimensions = 0;
            while (cl.isArray()) {
                dimensions++;
                cl = cl.getComponentType();
            }
            StringBuffer sb = new StringBuffer();
            sb.append(cl.getName());
            for (int i = 0; i < dimensions; i++) {
                sb.append("[]");
            }
            return sb.toString();
        } catch (Throwable e) { /*FALLTHRU*/ }
    }
    return type.getName();
}

4 个答案:

答案 0 :(得分:4)

StringBufferJDK 1.0以来一直存在。

Field来到pre-1.4.2

最后StringBuilder进入了Java 1.5

答案 1 :(得分:4)

主要原因是JVM具有先进的技术,以确定是否可以避免必须执行上下文所隐含的各种事情。由于StringBuffer是永远不会转义方法的局部变量,因此JVM可以安全地避免在输入StringBuffer的synchronized方法之前尝试获取对象的锁定 - 因为没有其他线程能够调用此特定方法StringBuffer的实例。

快速微观基准证明了这一点。使buffer一个字段会使以下代码减慢50%。

private void doTest(String toCopy) {
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < toCopy.length(); i++) {
        buffer.append(toCopy.charAt(i));
    }
    buffer.toString();
}

使用一百万个长度的字符串和1000次重复,上面的代码在我的机器上运行8秒钟。但是,一旦buffer成为字段而不是局部变量,则需要大约13秒(因为JVM不再能够轻松保证buffer只能由一个线程访问)。

答案 2 :(得分:2)

如果内存对我有用,首先会引入Stringbuffer,这可能是一段较旧的代码。真正的原因必须是线程安全,尽管stringbuilder不是rarad安全而缓冲区是。

答案 3 :(得分:1)

我见过并问过几个JVM开发人员为什么仍然使用StringBuffer,因为他们建议人们在8年前作为替代品迁移到StringBuilder。我觉得这不是他们担心/想到的事情。其中一位产品经理没有正式理由。

我认为,当Java编码约定(1999)建议您应该使用空格时,JDK源使用制表符的原因。修复程序使用代码格式化程序是微不足道的,但它不在任何人的待办事项列表中。


IMHO StringBuffer首先制作多线程绝不是一个好主意。在极少数情况下,您可能希望以多线程方式写入内存中的字符流,而您并不关心它生成的文本是如何混乱的,您仍然可以使用更自然的替代方案,如StringWriter(不是'在Java 1.0中可用或同步StringBuilder。

我认为它确实存在很多错误,例如: SimpleDateFormat使用它并且仍然在某种程度上使用StringBuffer,即使它不是线程安全的。它可能使一些开发人员使用线程安全集合或看起来类似线程安全的错误安全感。即使多个线程中使用StringBuffer而不是StringBuilder,即使可能存在错误,也更有可能通过简单的测试。

e.g。考虑两个线程编写

sb.append("Hello ").append("World").append("\n");

问题是,虽然每个append都是同步的,但每个代码块都没有。所以你可以得到

Hello Hello World World\n\n

Hello World Hello \nWorld\n

所以StringBuffer只有线程安全而没有同步,如果你只使用append一次使它变得毫无意义。


我试图指出,当涉及StringBuffer的问题发布在SO上时,人们会切换。