最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5 StringBuffer - 线程安全的可变字符序列
时间:2026-05-30 15:45:01 编辑:袖梨 来源:一聚教程网
作为Java核心类库中的线程安全字符串处理工具,StringBuffer通过同步机制和缓存优化在多线程场景下展现出独特优势。
StringBuffer —— 线程安全的可变字符串
一、StringBuffer 的定位
继承自AbstractStringBuilder的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 StringBuilder | Buffer 所有公开方法 synchronized,Builder 无同步 |
| toStringCache 的作用 | 缓存 toString 结果,避免重复创建 char[] |
| 默认容量 | 无参构造:16;传入字符串:length + 16 |
| 为什么 final 类 | 防止子类破坏同步语义 |
| 序列化方式 | 自定义 writeObject/readObject + ObjectStreamField |
| 单线程是否推荐 StringBuffer | 不推荐,synchronized 锁获取有不可忽略的开销 |
StringBuffer凭借其线程安全特性和优化机制,在多线程字符串处理场景中展现出不可替代的价值,是Java开发者必须掌握的核心工具类。