一、核心概念

1.1 StringBuffer 是什么?

StringBuffer 是 Java 中用于处理可变字符串的类,位于 java.lang 包中。它与 String 不同,String 是不可变的,而 StringBuffer 是可变的,允许在原有对象基础上进行追加、插入、删除等操作。

1.2 线程安全性(同步机制)的核心

StringBuffer线程安全是其最核心的特性之一。它通过在所有公共方法上使用 synchronized 关键字来实现同步,确保在多线程环境下对同一个 StringBuffer 实例的操作是原子的,不会出现数据竞争或不一致。

关键点StringBuffer 的每个修改方法(如 append(), insert(), delete())都声明为 synchronized,因此多个线程同时调用这些方法时,会自动排队执行。

1.3 与 StringBuilder 的对比

特性 StringBuffer StringBuilder
线程安全 ✅ 是(同步) ❌ 否(非同步)
性能 相对较慢(因同步开销) 更快(无锁)
适用场景 多线程环境 单线程或线程局部使用

二、操作步骤(详细流程)

以下是如何在多线程环境中安全使用 StringBuffer 的详细步骤:

步骤 1:创建 StringBuffer 实例

StringBuffer sb = new StringBuffer();

或指定初始容量:

StringBuffer sb = new StringBuffer(32); // 初始容量32

步骤 2:在多线程中共享 StringBuffer 实例

// 共享的 StringBuffer 对象
StringBuffer sharedBuffer = new StringBuffer();

// 创建多个线程
Thread thread1 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        sharedBuffer.append("A");
    }
});

Thread thread2 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        sharedBuffer.append("B");
    }
});

步骤 3:启动线程并观察同步效果

thread1.start();
thread2.start();

// 等待线程结束
try {
    thread1.join();
    thread2.join();
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

// 输出结果(顺序不确定,但内容完整)
System.out.println("Final length: " + sharedBuffer.length()); // 应为 2000

步骤 4:验证线程安全

  • 尽管 AB 的顺序可能交错,但由于 append() 是同步的,不会出现字符丢失或乱码
  • 如果使用 StringBuilder,结果可能少于 2000(发生竞争)。

三、常见错误

❌ 错误 1:误以为 StringBuffer 在所有场景都高效

  • 问题:在单线程中使用 StringBuffer,造成不必要的同步开销。
  • 示例
    // 单线程中应使用 StringBuilder
    StringBuffer sb = new StringBuffer(); // ❌ 不必要
    StringBuilder sb = new StringBuilder(); // ✅ 推荐
    

❌ 错误 2:过度同步导致性能瓶颈

  • 问题:在高并发下,所有线程排队执行,导致性能下降。
  • 原因:每个 append() 都要获取对象锁。

❌ 错误 3:混淆 StringBuffer 与 String 的不可变性

  • 问题:认为 StringBuffer 赋值后不能修改。
  • 正解StringBuffer 本身可变,但引用可以重新指向。

四、注意事项

  1. 仅在多线程共享时使用 StringBuffer

    • 如果是局部变量或线程私有,优先使用 StringBuilder
  2. ⚠️ 避免在循环中频繁创建 StringBuffer

    • 创建对象有开销,应重用或预估容量。
  3. 🔐 同步范围是方法级别

    • synchronized 修饰的是整个方法,粒度较粗,不适合极高并发场景。
  4. 📏 合理设置初始容量

    • 默认容量为 16,频繁扩容影响性能。
    StringBuffer sb = new StringBuffer(1024); // 预分配大容量
    

五、使用技巧

技巧 1:链式调用

StringBuffer 的大多数方法返回 this,支持链式操作:

sb.append("Hello")
  .append(" ")
  .append("World")
  .insert(0, "Prefix: ");

技巧 2:转换为 String

String result = sb.toString(); // 安全且高效

技巧 3:清空内容

sb.setLength(0); // 快速清空,不创建新对象

六、最佳实践

✅ 实践 1:多线程拼接日志

public class Logger {
    private final StringBuffer logBuffer = new StringBuffer();

    public void log(String message) {
        logBuffer.append("[")
                 .append(Thread.currentThread().getName())
                 .append("] ")
                 .append(message)
                 .append("\n");
    }

    public String getLog() {
        return logBuffer.toString();
    }
}

✅ 实践 2:避免在方法内部使用 StringBuffer(单线程)

// ❌ 不推荐(单线程)
public String buildMessage() {
    StringBuffer sb = new StringBuffer();
    sb.append("Hello");
    sb.append("World");
    return sb.toString();
}

// ✅ 推荐
public String buildMessage() {
    StringBuilder sb = new StringBuilder();
    sb.append("Hello");
    sb.append("World");
    return sb.toString();
}

七、性能优化建议

  1. 预设容量:减少内部数组扩容次数。

    StringBuffer sb = new StringBuffer(512);
    
  2. 减少 synchronized 调用次数

    • 合并多次操作为一次。
    // ❌ 多次同步
    sb.append("a");
    sb.append("b");
    sb.append("c");
    
    // ✅ 一次同步(如果内容已知)
    sb.append("abc");
    
  3. 考虑使用 StringBuilder + 外部同步(如 Collections.synchronizedList 模式):

    StringBuilder sb = new StringBuilder();
    synchronized (sb) {
        sb.append("data");
    }
    

    但通常不如直接用 StringBuffer 简洁。

  4. 高并发场景考虑其他方案

    • 使用 ThreadLocal<StringBuilder> 避免共享。
    • 或使用无锁数据结构(如 ConcurrentLinkedQueue 存储字符串片段)。

总结

项目 说明
核心优势 线程安全,适合多线程字符串拼接
实现机制 所有公共方法使用 synchronized 同步
适用场景 多线程共享的字符串构建
替代方案 单线程用 StringBuilder,高性能用 ThreadLocal
性能提示 预设容量、减少同步调用、避免滥用

💡 一句话总结StringBuffer 是线程安全的可变字符串容器,通过方法级同步保障多线程安全,但在单线程中应优先使用 StringBuilder 以获得更好性能。