1

我正在查看 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

4 回答 4

4

StringBuffer自JDK 1.0以来一直存在。

Field出现在1.4.2 之前

终于StringBuilderJava 1.5中成功了。

于 2012-08-10T21:50:01.307 回答
4

主要原因是 JVM 具有先进的技术,可以查看是否可以避免必须执行上下文所暗示的各种事情。由于 StringBuffer 是一个从不逃避方法的局部变量,JVM 可以安全地避免在进入 StringBuffer 的同步方法之前尝试获取对象上的锁——因为没有其他线程能够调用这个特定的方法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只能由一个线程访问)。

于 2012-08-10T21:51:24.127 回答
2

如果我没记错的话,首先引入了 Stringbuffer,所以这可能是一段较旧的代码。真正的原因必须是线程安全,尽管当缓冲区是字符串生成器时,它不是安全的。

于 2012-08-10T21:44:25.323 回答
1

我遇到并询问了几个 JVM 开发人员,为什么仍然有这么多使用 StringBuffer,因为他们建议人们迁移到 StringBuilder 作为 8 年前的替代品。我的印象不是他们担心/考虑过的事情。其中一位产品经理没有给出官方理由。

我认为当 Java 编码约定 (1999) 建议您应该使用空格时,为什么 JDK 源代码使用制表符。使用代码格式化程序进行修复很简单,但它不在任何人的待办事项列表中。


恕我直言,StringBuffer 从一开始就不是多线程的好主意。在极少数情况下,您可能希望以多线程方式写入内存中的字符流,并且您不在乎它生成的文本有多混乱,您仍然可以使用更自然的替代方案,例如 StringWriter(不是t 在 Java 1.0 中可用)或同步 StringBuilder。

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

例如考虑两个线程写入

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

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

Hello Hello World World\n\n

或者

Hello World Hello \nWorld\n

因此,如果您仅使用 append 一次,则 StringBuffer 仅在没有同步的情况下是线程安全的,这使得它变得毫无意义。


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

于 2012-08-11T07:19:41.153 回答