一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

5 StringBuffer - 线程安全的可变字符序列

时间:2026-05-30 15:45:01 编辑:袖梨 来源:一聚教程网

作为Java核心类库中的线程安全字符串处理工具,StringBuffer通过同步机制和缓存优化在多线程场景下展现出独特优势。

StringBuffer —— 线程安全的可变字符串


一、StringBuffer 的定位

继承自AbstractStringBuilder的StringBuffer,其核心价值在于提供线程安全的字符串操作能力。

5 StringBuffer —— 线程安全的可变字符串

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

final修饰符确保其不可继承,从而维护线程安全特性不被破坏。

String、StringBuilder、StringBuffer 三者关系

                    ┌─────────────┐
                    │   String    │  不可变,线程天然安全
                    └─────────────┘
                    ┌──────────────────────┐
                    │ AbstractStringBuilder│  骨架实现(扩容/追加/删除等)
                    └──────────────────────┘
                     ↙                     ↘
        ┌──────────────┐           ┌──────────────┐
        │ StringBuffer │           │ StringBuilder│
        │ synchronized │           │   非同步     │
        │ 多线程安全    │           │ 单线程高效    │
        └──────────────┘           └──────────────┘

二、构造方法与初始容量

public StringBuffer() {
    super(16);  // 默认容量 16
}public StringBuffer(int capacity) {
    super(capacity);
}public StringBuffer(String str) {
    super(str.length() + 16);  // 关键:字符串长度 + 16
    append(str);
}public StringBuffer(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}
public class CapacityDemo {
    public static void main(String[] args) {
        StringBuffer sb1 = new StringBuffer();
        System.out.println("空构造容量: " + sb1.capacity());  // 16        StringBuffer sb2 = new StringBuffer("Hello");
        System.out.println("带字符串容量: " + sb2.capacity()); // 5 + 16 = 21
        System.out.println("有效长度: " + sb2.length());       // 5
    }
}

三、synchronized 同步策略

所有涉及数据修改的公开方法均采用synchronized关键字进行同步控制:

public synchronized StringBuffer append(String str) {
    toStringCache = null;       // 清除缓存
    super.append(str);
    return this;
}public synchronized StringBuffer insert(int offset, String str) {
    toStringCache = null;
    super.insert(offset, str);
    return this;
}public synchronized StringBuffer delete(start, int end) {
    toStringCache = null;
    super.delete(start, end);
    return this;
}public synchronized StringBuffer reverse() {
    toStringCache = null;
    super.reverse();
    return this;
}

同步成本分析

public class SyncOverheadDemo {
    public static void main(String[] args) throws InterruptedException {
        int iterations = 100_000;        // 单线程 StringBuffer
        StringBuffer buffer = new StringBuffer();
        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            buffer.append("x");
        }
        long tBuffer = System.nanoTime() - start;        // 单线程 StringBuilder
        StringBuilder builder = new StringBuilder();
        start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            builder.append("x");
        }
        long tBuilder = System.nanoTime() - start;        System.out.printf("StringBuffer: %6.2f ms%n", tBuffer / 1_000_000.0);
        System.out.printf("StringBuilder:%6.2f ms%n", tBuilder / 1_000_000.0);
    }
}

四、toString 缓存机制

这是StringBuffer区别于StringBuilder的重要特性。

// StringBuffer 中定义的缓存字段
private transient char[] toStringCache;public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);  // 使用包私有构造,共享数组
}// 任何修改操作都会清除缓存
public synchronized StringBuffer append(String str) {
    toStringCache = null;  // 失效缓存
    super.append(str);
    return this;
}

toStringCache仅适用于多线程场景,StringBuilder因单线程特性无需此类开销:

// StringBuilder 的 toString —— 每次都创建新副本
public String toString() {
    return new String(value, 0, count);  // 不缓存,不共享
}

缓存带来的性能差异

public class ToStringCacheDemo {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello World");
        int iterations = 10_000_000;        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            sb.toString();  // 第二次起命中 toStringCache
        }
        long elapsed = System.nanoTime() - start;
        System.out.printf("StringBuffer.toString × %d: %.2f ms%n",
                iterations, elapsed / 1_000_000.0);
    }
}

五、序列化机制

StringBuffer采用定制化的序列化方案:

private static final java.io.ObjectStreamField[] serialPersistentFields = {
    new java.io.ObjectStreamField("value", char[].class),
    new java.io.ObjectStreamField("count", Integer.TYPE),
    new java.io.ObjectStreamField("shared", Boolean.TYPE),
};private synchronized void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
    java.io.ObjectOutputStream.PutField fields = s.putFields();
    fields.put("value", value);
    fields.put("count", count);
    fields.put("shared", false);
    s.writeFields();
}private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
    java.io.ObjectInputStream.GetField fields = s.readFields();
    value = (char[]) fields.get("value", null);
    count = fields.get("count", 0);
}

serialPersistentFields声明序列化字段,putFields/writeFields提供更精细的序列化控制。


六、综合实战:并发日志收集器

import java.util.concurrent.*;/**
 * 基于 StringBuffer 的线程安全日志收集器
 * 演示多写一读场景下的正确使用
 */
public class ConcurrentLogCollector {    private final StringBuffer logBuffer = new StringBuffer(4096);
    private final ScheduledExecutorService flusher
            = Executors.newSingleThreadScheduledExecutor();    public ConcurrentLogCollector() {
        // 每 5 秒自动冲洗到控制台
        flusher.scheduleAtFixedRate(this::flush, 5, 5, TimeUnit.SECONDS);
    }    public void log(String level, String message) {
        // StringBuffer 自身是线程安全的,多个线程可安全并发调用
        logBuffer.append(java.time.LocalTime.now())
                .append(" [").append(level).append("] ")
                .append(message)
                .append(System.lineSeparator());
    }    public synchronized String flush() {
        if (logBuffer.length() == 0) {
            return "";
        }
        String snapshot = logBuffer.toString();
        logBuffer.setLength(0);  // 清空缓冲区
        return snapshot;
    }    public void shutdown() {
        flusher.shutdown();
        System.out.println("=== 最终日志冲洗 ===");
        System.out.print(flush());
    }    public static void main(String[] args) throws InterruptedException {
        ConcurrentLogCollector collector = new ConcurrentLogCollector();        ExecutorService workers = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 20; i++) {
            final int taskId = i;
            workers.submit(() -> {
                collector.log("INFO", "任务 #" + taskId + " 执行中");
            });
        }        workers.shutdown();
        workers.awaitTermination(3, TimeUnit.SECONDS);
        collector.shutdown();
    }
}

七、面试高频考点

问题关键要点
StringBuffer vs StringBuilderBuffer 所有公开方法 synchronized,Builder 无同步
toStringCache 的作用缓存 toString 结果,避免重复创建 char[]
默认容量无参构造:16;传入字符串:length + 16
为什么 final 类防止子类破坏同步语义
序列化方式自定义 writeObject/readObject + ObjectStreamField
单线程是否推荐 StringBuffer不推荐,synchronized 锁获取有不可忽略的开销

StringBuffer凭借其线程安全特性和优化机制,在多线程字符串处理场景中展现出不可替代的价值,是Java开发者必须掌握的核心工具类。

热门栏目