2012-08-10 68 views
1

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

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

它可能写在Java5之前(或者在写作的时候不知道Java5的人)? – 2012-08-10 21:42:46

+0

你将不得不问作者。你在这里读到的任何东西都只是猜测而已。不建设性。 – EJP 2012-08-11 02:15:59

回答

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将只能被一个线程访问)。

+0

这可能也可能不是代码被这样写的原因。你有这方面的证据吗? – EJP 2012-08-11 02:14:39

+0

http://www.infoq.com/articles/java-threading-optimizations-p1显示了由JVM执行的与线程问题(包括逃逸分析)相关的一些选择。作为一个方面说明,当使用StringBuffer和使用StringBuilder时,我的微型基准测试执行完全一样。 – Dunes 2012-08-11 09:09:44

2

如果没记错的StringBuffer首次引入所以这可能是一个旧的代码。真正的原因必须是线程安全的,尽管在缓冲区时stringbuilder不是安全的。

+1

局部变量是线程安全的有什么意义? – evg 2012-08-10 21:47:15

4

StringBufferJDK 1.0以来一直在。

Field来到pre-1.4.2

最后StringBuilderJava 1.5中取得了进展。

+1

Field在1.2中出现了Reflection。 – EJP 2012-08-11 02:13:35

1

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

我认为它的原因是JDK源代码在Java编码公约(1999)建议您应该使用空格时使用制表符。该修补程序使用代码格式化程序很简单,但不适用于任何人的待办事项列表。


恕我直言,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 

所以StringBuffer的仅仅是线程安全的不同步,如果你只使用一次,追加这使得它毫无意义。


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

相关问题