性能瓶颈分析
- 锁竞争:
StringBuffer
是线程安全的,其方法大多使用 synchronized
关键字修饰。在多线程高并发访问共享字符串进行拼接操作时,大量线程会竞争 StringBuffer
对象的锁,导致线程阻塞,降低并发性能。例如,多个线程同时调用 append
方法时,只有获取到锁的线程才能执行操作,其他线程需等待,造成锁竞争开销。
- 频繁扩容:
StringBuffer
内部维护一个字符数组,初始容量为16。当拼接的字符超过当前容量时,会进行扩容操作。扩容需要重新分配内存、复制原数组内容,这在高并发场景下频繁发生会消耗大量时间和资源。
优化方法
- 使用
StringBuilder
替代(若线程安全非必须):如果业务场景允许非线程安全的字符串拼接,StringBuilder
是更好的选择。StringBuilder
与 StringBuffer
功能类似,但不具备线程安全性,因此没有锁开销,性能更高。例如在单线程或使用线程局部变量的场景中,可直接使用 StringBuilder
。
- 减少锁竞争:采用锁分离技术,例如将共享
StringBuffer
按照一定规则拆分,每个线程操作自己对应的 StringBuffer
,最后再合并结果。这样可以减少线程间对同一锁的竞争。
- 合理预估容量:在创建
StringBuffer
时,尽量根据实际需求预估字符串的长度,设置合适的初始容量,避免频繁扩容。如已知要拼接的字符串大概长度为1000,可 new StringBuffer(1000)
。
Java标准库中使用 StringBuffer
保证线程安全的类或方法举例
PrintStream
类:在 PrintStream
的 print
和 println
方法中,会使用 StringBuffer
来拼接输出内容,以保证多线程环境下输出的正确性和线程安全性。例如:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
private void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
private void write(String s) {
try {
synchronized (this) {
ensureOpen();
StringBuffer sb = new StringBuffer(s);
for (int i = 0; i < sb.length(); i++) {
write(sb.charAt(i));
}
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
}
Formatter
类:Formatter
类在格式化字符串输出时,内部会使用 StringBuffer
进行字符串拼接操作,确保多线程环境下格式化输出的线程安全性。