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

Java StringBuffer 類原始碼深度剖析:線程安全、擴容機制、核心方法

     編輯:Java 相關知識

前言

StringBuffer 是 Java 1.0 就存在的線程安全可變字串建構器。本文基於 JDK 1.6/1.8 原始碼,深度剖析其類別結構、同步機制、擴容策略、核心方法實現,並與 StringBuilder、AbstractStringBuilder 關係圖解說明。

類別繼承體系

java.lang.Object
  └── java.lang.AbstractStringBuilder (包私有,核心邏輯)
        ├── java.lang.StringBuffer (final, synchronized)
        └── java.lang.StringBuilder (final, 非同步)

關鍵定義


public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
  • final:不可被繼承
  • 實作 Serializable、CharSequence
  • 核心邏輯委派給父類 AbstractStringBuilder

同步機制分析

方法級同步


// 幾乎所有公共方法皆加 synchronized
public synchronized int length() { return count; }
public synchronized int capacity() { return value.length; }
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
public synchronized StringBuffer insert(int index, char[] str, int offset, int len) {
    toStringCache = null;
    super.insert(index, str, offset, len);
    return this;
}
public synchronized StringBuffer delete(int start, int end) {
    toStringCache = null;
    super.delete(start, end);
    return this;
}
public synchronized String toString() {
    // 複製字元陣列確保不可變快照
    return new String(value, 0, count);
}

同步粒度與效能影響

  • 粒度:方法級 (粗粒度)
  • 優點:簡單、安全、無死鎖風險
  • 缺點:單線程場景下鎖開銷明顯 (約 10-30%)
  • JVM 優化:偏向鎖、輕量級鎖、鎖消除 (逃逸分析) 可部分抵消

建構子詳解


// 1. 無參:預設容量 16
public StringBuffer() {
    super(16);
}

// 2. 指定初始容量
public StringBuffer(int capacity) {
    super(capacity);
}

// 3. 從字串初始化:容量 = 字串長度 + 16
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

// 4. 從 CharSequence 初始化 (JDK 5+)
public StringBuffer(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

測試驗證


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer();
    System.out.println(s1.capacity()); // 16

    StringBuffer s2 = new StringBuffer(100);
    System.out.println(s2.capacity()); // 100

    StringBuffer s3 = new StringBuffer("abc");
    System.out.println(s3.capacity()); // 19 (3+16)
    System.out.println(s3);            // abc
}

核心操作委派機制

所有實質字元操作 (append、insert、delete、reverse 等) 均委派給 AbstractStringBuilder:


public StringBuffer append(String str) {
    toStringCache = null;        // 使 toString 快取失效
    super.append(str);           // 父類實際處理
    return this;                 // 支援鏈式呼叫
}

toString 快取機制 (JDK 1.6+)


private transient char[] toStringCache; // 不序列化

public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOf(value, count); // 複製快照
    }
    return new String(toStringCache);
}
  • 避免重複複製字元陣列
  • 任何修改操作 (append/insert/delete) 將 toStringCache = null 失效
  • 線程安全:同步方法保證快取一致性

擴容策略 (AbstractStringBuilder)


private void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    if (newCapacity < 0) { // 溢位處理
        newCapacity = Integer.MAX_VALUE;
    } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
    value = Arrays.copyOf(value, newCapacity);
}
  • 擴容公式:新容量 = 舊容量 * 2 + 2
  • 預設初始 16 → 34 → 70 → 142 → 286 → ...
  • 建議預估大概長度,建構子指定初始容量避免多次擴容複製

關鍵方法原始碼精讀

append 核心流程


// AbstractStringBuilder.append(String)
public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len); // 擴容檢查
    str.getChars(0, len, value, count);  // 字元複製
    count += len;
    return this;
}

insert 指定位置插入


public AbstractStringBuilder insert(int index, String str) {
    if (index < 0 || index > count) throw new StringIndexOutOfBoundsException();
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len);
    // 後段元素右移
    System.arraycopy(value, index, value, index + len, count - index);
    // 插入新字元
    str.getChars(0, len, value, index);
    count += len;
    return this;
}

delete 區間刪除


public AbstractStringBuilder delete(int start, int end) {
    if (start < 0) start = 0;
    if (end > count) end = count;
    if (start > end) throw new StringIndexOutOfBoundsException();
    int len = end - start;
    if (len > 0) {
        // 後段元素左移覆蓋
        System.arraycopy(value, start + len, value, start, count - end);
        count -= len;
    }
    return this;
}

reverse 反轉


public AbstractStringBuilder reverse() {
    boolean hasSurrogate = false;
    int n = count - 1;
    for (int j = (n - 1) >> 1; j >= 0; j--) {
        char temp = value[j];
        char temp2 = value[n - j];
        if (!hasSurrogate) {
            // 處理代理對 (Supplementary Characters)
            if (Character.isLowSurrogate(temp) || Character.isHighSurrogate(temp2)) {
                hasSurrogate = true;
            }
        }
        value[j] = temp2;
        value[n - j] = temp;
    }
    if (hasSurrogate) {
        // 修正代理對順序
        for (int j = 0; j < count; j++) {
            char c = value[j];
            if (Character.isHighSurrogate(c)) {
                // 交換高低代理位置
            }
        }
    }
    return this;
}

StringBuffer vs StringBuilder vs String 總結

維度StringStringBuilderStringBuffer
可變性不可變可變可變
線程安全天然安全不安全方法級同步
效能拼接慢最快次快
繼承finalfinalfinal
JDK 版本1.05.01.0
toString自身新建 String快取 + 新建
適用場景常量、參數單線程建構多線程共享建構

現代使用建議

  1. 單線程:StringBuilder (預設)、ThreadLocal (高併發復用)
  2. 多線程共享:StringBuffer 或 Collections.synchronizedList() 包裝 StringBuilder
  3. 日誌/監控:考慮 StringJoiner (Java 8) 或 String.format()
  4. 大字串:CharArrayWriter / ByteArrayOutputStream 避免字元編碼開銷

總結

StringBuffer 以方法級同步實現線程安全,核心邏輯委派給 AbstractStringBuilder,擴容策略為 2n+2,toString 採用快取機制。現代開發 95% 場景應優先選用 StringBuilder,僅在確定多線程共享同一建構器實例時使用 StringBuffer。理解其原始碼結構有助於排查併發問題、優化字串操作效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved