首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

String、StringBuffer、StringBuilder 深度對比:線程安全、性能、JDK 優化與選型決策

     編輯:Java 綜合教學

String、StringBuffer、StringBuilder 深度對比:線程安全、性能、JDK 優化與選型決策

字符串處理三巨頭是 Java 基礎必考點。本文從底層實現、線程安全模型、JDK 版本優化、實測性能、編譯器語法糖五個維度深度對比,給出生產環境選型決策樹。

一、核心差異總覽

特性StringStringBufferStringBuilder
可變性不可變可變可變
線程安全天然安全 (不可變)同步方法,安全非同步,不安全
繼承關係final classextends AbstractStringBuilderextends AbstractStringBuilder
JDK 引入1.01.05.0
底層存儲byte[] (JDK 9+) / char[]byte[] (JDK 9+) / char[]byte[] (JDK 9+) / char[]
擴容策略N/A2倍 + 22倍 + 2
主要用途常量、Key、少量拼接多線程共享緩衝區單線程大量拼接

二、底層實現深度解析

1. AbstractStringBuilder 共同父類

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    byte[] value; // JDK 9+ 緊湊字符串
    byte coder;   // LATIN1 或 UTF16
    int count;    // 字符數量
    
    // 核心擴容邏輯
    private void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity;
        if (newCapacity < 0) newCapacity = Integer.MAX_VALUE;
        value = Arrays.copyOf(value, newCapacity);
    }
    
    // append 核心:確保容量 → 編碼寫入 → count++
    public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        putStringAt(count, str, coder);
        count += len;
        return this;
    }
}

2. StringBuffer:同步鎖開銷

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null; // 緩存失效
    super.append(str);
    return this;
}

@Override
public synchronized int length() { return count; } // 連讀取都加鎖!

每個公共方法都加 synchronized,即便是 length()、charAt() 也加鎖,高併發下鎖爭用嚴重。

3. StringBuilder:零同步,極致性能

// 無 synchronized 修飾
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

繼承父類所有功能,但完全移除同步,單線程下性能接近 C 級別。

三、JDK 版本關鍵優化時間線

  • JDK 5:引入 StringBuilder,替代 StringBuffer 成為首選
  • JDK 6:偏向鎖、輕量級鎖優化 StringBuffer 同步開銷
  • JDK 7:substring 複製數組,修復內存洩漏
  • JDK 9:緊湊字符串 - byte[] + coder 替代 char[],Latin-1 字符節省 50% 內存
  • JDK 11:String.repeat()、strip() 等新 API
  • JDK 13:StringBuilder 內部優化 putStringAt 向量化

四、編譯器語法糖:+ 拼接的真相

// 源碼
String s = "a" + "b" + c + "d";

// 反編譯 (JDK 8-)
String s = new StringBuilder().append("a").append("b").append(c).append("d").toString();

// 反編譯 (JDK 9+ invokedynamic)
String s = StringConcatFactory.makeConcatWithConstants(...); // 調用點綁定,更靈活
  • 常量折疊:"a" + "b" 編譯期直接合併為 "ab"
  • 變量拼接:運行時自動生成 StringBuilder 代碼
  • 循環陷阱:循環中 s += "x" 每次迭代創建新 StringBuilder → 性能災難

五、實測性能數據 (JMH, 100萬次 append)

場景String +StringBufferStringBuilder
單線程循環拼接~12,000 ms~8 ms~5 ms
多線程共享拼接 (10線程)N/A~45 ms數據錯誤/崩潰
預分配容量 (100萬)N/A~4 ms~3 ms

六、選型決策樹

需要拼接字符串?
├─ 少量、常量、非熱點 → 直接用 + (可讀性最高,編譯器優化)
├─ 大量拼接 / 熱點路徑?
│   ├─ 單線程 / 局部變量 / 線程封閉 → StringBuilder (性能王者)
│   │   └─ 能預估大致長度? → new StringBuilder(estimatedSize) (避免擴容)
│   └─ 多線程共享同一緩衝區 → StringBuffer (或 ThreadLocal + 合併)
└─ 需要不可變、作為 Key、常量池複用 → String

七、進階替代方案

  • ThreadLocal:每線程獨享 Builder,最後合併,避免鎖爭用
  • java.io.StringWriter:實現 Writer 接口,適配流式 API
  • com.google.common.base.Joiner (Guava):Joiner.on(",").skipNulls().join(list)
  • String.join() (JDK 8+):String.join(",", "a", "b", "c")
  • Collectors.joining() (Stream):list.stream().collect(Collectors.joining(","))

八、常見面試陷阱

  1. StringBuffer sb = new StringBuffer(); sb.append(null); 存入什麼?"null" 字符串 (非 null 值)
  2. StringBuilder 線程不安全表現?數據丟失、數組越界、count 錯誤、死循環
  3. 如何將 StringBuilder 轉 String 無拷貝?JDK 9+ 無法完全避免 (toString() 必須複製 byte[] 保證不可變)
  4. StringBuilder 可繼承嗎?非 final,但不建議繼承 (內部無防禦性拷貝)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved