2012-08-10 65 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上發佈時要切換。

相關問題